golang游戏开发学习笔记-开发一个简单的2D游戏(完成篇)

//检测一个物体是否与地图中的方块发生碰撞

func (gameMap *GameMap) IsColl(gameObj GameObj,shift mgl32.Vec2)(bool,mgl32.Vec2){

position := gameObj.GetPosition();

size := gameObj.GetSize()

startX,endX,startY,endY := gameMap.FetchBox(mgl32.Vec2{position[0],position[1]},mgl32.Vec2{size[0],size[1]})

for i:=startX;i<=endX;i++{

for j := startY;j<endY;j++{

block := gameMap.blocks[int(i)][int(j)]

if(block != nil){

isCol,position := physic.ColldingAABBPlace(gameObj,block,shift)

if(isCol){

return isCol,position

}

}

}

}

return false,gameObj.GetPosition()

}

三.主角的诞生

现在要来创造我们的主角了,首先创建一个类代表游戏中所有可移动的物体

package model

import(

“game2D/resource”

“game2D/constant”

“github.com/go-gl/mathgl/mgl32”

)

//可移动的游戏对象

type MoveObj struct{

GameObj

//在上下左右方向是否可移动

stockUp,stockDown,stockLeft,stockRight bool

//水平移动速度

movementSpeed float32

//飞行速度

fallSpeed float32

//下坠速度

flySpeed float32

//移动时的动画纹理

moveTextures []*resource.Texture2D

//静止时的纹理

stantTexture *resource.Texture2D

//游戏地图

gameMap *GameMap

//当前运动帧

moveIndex int

//运动帧之间的切换阈值

moveDelta float32

}

func NewMoveObject(gameObj GameObj,movementSpeed,flySpeed float32, moveTextures []*resource.Texture2D,gameMap *GameMap) *MoveObj{

moveObj := &MoveObj{GameObj:gameObj,

movementSpeed:movementSpeed,

fallSpeed:100,

gameMap:gameMap,

moveTextures:moveTextures,

flySpeed:flySpeed,

moveIndex:0,

moveDelta:0,

stantTexture:gameObj.texture}

return moveObj

}

//恢复静止

func (moveObj *MoveObj) Stand(){

moveObj.texture = moveObj.stantTexture

}

//由用户主动发起的运动

func(moveObj *MoveObj) Move(direction constant.Direction, delta float32){

shift := mgl32.Vec2{0,0}

if(direction ==constant. DOWN){

if(!moveObj.stockDown && moveObj.y + moveObj.size[1] < moveObj.gameMap.Height){

shift[1] += moveObj.flySpeed * delta

}

}

if(direction == constant.UP){

if(!moveObj.stockUp && moveObj.y > 0){

shift[1] -= moveObj.flySpeed * delta

}

}

if(direction == constant.LEFT){

moveObj.ReverseX()

if(moveObj.moveIndex >= len(moveObj.moveTextures)){

moveObj.moveIndex = 0

}

moveObj.moveDelta += delta

if(moveObj.moveDelta > 0.1){

moveObj.moveDelta = 0

moveObj.texture = moveObj.moveTextures[moveObj.moveIndex]

moveObj.moveIndex += 1

}

if(!moveObj.stockLeft && moveObj.x > 0){

shift[0] -= moveObj.movementSpeed * delta

}

}

if(direction == constant.RIGHT){

moveObj.ForWardX()

if(moveObj.moveIndex >= len(moveObj.moveTextures)){

moveObj.moveIndex = 0

}

moveObj.moveDelta += delta

if(moveObj.moveDelta > 0.1){

moveObj.moveDelta = 0

moveObj.texture = moveObj.moveTextures[moveObj.moveIndex]

moveObj.moveIndex += 1

}

if(!moveObj.stockRight && moveObj.x + moveObj.size[0] < moveObj.gameMap.Width){

shift[0] += moveObj.movementSpeed * delta

}

}

isCol,position := moveObj.gameMap.IsColl(moveObj.GameObj,shift)

if(isCol){

moveObj.SetPosition(position)

}else{

moveObj.x += shift[0]

moveObj.y += shift[1]

}

}

这个类只有一个特殊的move方法,传入一个代表方向的常量和帧与帧之间的延迟,当往左或往右运动时会将自身纹理切换为运动的纹理并在度过指定时间之后切换运动帧来形成动画效果,当静止时调用Stand方法会将纹理帧切换为静止时的图像。注意,在往左或又运动时我们只需要将纹理动画直接求镜像而不用创建新的纹理。当然这种动画的实现方式是不太优雅的,更好的方法是调用gl.SubTextImage方法将纹理图像直接替换为指定的图像而不是切换纹理,或者将多张图片拼成为一张,在运动时调整纹理坐标来显示不同图像。不过理解了这一种,其他两种也不会有问题。

4.还不够抽象

目前为止,我们已经有了地图,游戏角色,摄像头和精灵渲染器,但在man方法里直接创建和修改这些对象似乎不太优雅,我们创建一个Game类来进一步封装

package game

import(

“game2D/resource”

“game2D/sprite”

“game2D/camera”

“game2D/model”

“game2D/constant”

“github.com/go-gl/mathgl/mgl32”

“github.com/go-gl/gl/v4.1-core/gl”

“github.com/go-gl/glfw/v3.2/glfw”

)

type GameState int

const(

GAME_ACTIVE GameState = 0

GAME_MENU GameState = 1

)

type Game struct{

//游戏状态

state GameState

//屏幕大小

screenWidth, screenHeight float32

//世界大小

worldWidth, worldHeight float32

//精灵渲染器

renderer *sprite.SpriteRenderer

//游戏地图

gameMap *model.GameMap

//摄像头

camera *camera.Camera2D

//玩家

player *model.MoveObj

//按键状态

Keys [1024]bool

}

func NewGame(screenWidth, screenHeight, wordWidth, wordHeight float32) *Game{

game := Game{screenWidth:screenWidth,

screenHeight:screenHeight,

worldWidth:wordWidth,

worldHeight:wordHeight,

state:GAME_ACTIVE}

return &game

}

func (game *Game) Init(){

//初始化着色器

resource.LoadShader(“./glsl/shader.vs”, “./glsl/shader.fs”, “sprite”)

shader := resource.GetShader(“sprite”)

shader.Use()

shader.SetInt(“image”, 0)

//设置投影

projection := mgl32.Ortho(0, float32(game.screenWidth),float32(game.screenHeight),0, -1, 1)

shader.SetMatrix4fv(“projection”, &projection[0])

//初始化精灵渲染器

game.renderer = sprite.NewSpriteRenderer(shader)

//加载资源

resource.LoadTexture(gl.TEXTURE0,“./image/stone.png”,“stone”)

resource.LoadTexture(gl.TEXTURE0,“./image/soil.png”,“soil”)

resource.LoadTexture(gl.TEXTURE0,“./image/man-stand.png”,“man-stand”)

resource.LoadTexture(gl.TEXTURE0,“./image/1.png”,“1”)

resource.LoadTexture(gl.TEXTURE0,“./image/2.png”,“2”)

resource.LoadTexture(gl.TEXTURE0,“./image/3.png”,“3”)

resource.LoadTexture(gl.TEXTURE0,“./image/4.png”,“4”)

resource.LoadTexture(gl.TEXTURE0,“./image/5.png”,“5”)

resource.LoadTexture(gl.TEXTURE0,“./image/6.png”,“6”)

//创建游戏地图

game.gameMap = model.NewGameMap(game.worldWidth, game.worldHeight,“testMapFile”)

//创建测试游戏人物

gameObj := model.NewGameObj(resource.GetTexture(“man-stand”),

game.worldWidth/2,

game.worldHeight/2,

&mgl32.Vec2{70,100},

0,

&mgl32.Vec3{1,1,1})

//创建摄像头,将摄像头同步到玩家位置

game.camera = camera.NewDefaultCamera(game.worldHeight,

game.worldWidth,

game.screenWidth,

game.screenHeight,

mgl32.Vec2{game.worldWidth/2 - game.screenWidth/2, game.worldHeight/2 - game.screenHeight/2})

game.player = model.NewMoveObject(*gameObj,1000,1000,[]*resource.Texture2D{resource.GetTexture(“1”),

resource.GetTexture(“2”),

resource.GetTexture(“3”),

resource.GetTexture(“4”),

resource.GetTexture(“5”),

resource.GetTexture(“6”),},game.gameMap)

}

//处理输入

func (game *Game) ProcessInput(delta float64){

if(game.state == GAME_ACTIVE){

playerMove := false

if(game.Keys[glfw.KeyA]){

playerMove = true

game.player.Move(constant.LEFT,float32(delta))

}

if(game.Keys[glfw.KeyD]){

playerMove = true

game.player.Move(constant.RIGHT,float32(delta))

}

if(game.Keys[glfw.KeyW]){

playerMove = true

game.player.Move(constant.UP,float32(delta))

}

if(game.Keys[glfw.KeyS]){

playerMove = true

game.player.Move(constant.DOWN,float32(delta))

}

if(!playerMove){

game.player.Stand()

}

}

}

func (game *Game) Update(delta float64){

}

//渲染每一帧

func (game *Game) Render(delta float64){

resource.GetShader(“sprite”).SetMatrix4fv(“view”,game.camera.GetViewMatrix())

//game.player.MoveBy(float32(delta))

game.player.Draw(game.renderer)

//摄像头跟随

position := game.player.GetPosition()

size := game.player.GetSize()

game.camera.InPosition(position[0] - game.screenWidth /2 + size[0], position[1] - game.screenHeight / 2 + size[1])

game.gameMap.Draw(game.camera.GetPosition(),

mgl32.Vec2{game.screenWidth,game.screenHeight},

game.renderer)

}

game类中我们在初始化函数里加载资源创建地图和其他需要的对象,在渲染函数中始终将摄像头的位置与主角位置同步,并在ProcessInput方法内将按键信息转化为方向信息

5.动起来

万事俱备,现在要做的是在main方法内让程序跑起来了

package main

import(

“github.com/go-gl/glfw/v3.2/glfw”

“github.com/go-gl/gl/v4.1-core/gl”

“game2D/game”

“runtime”

)

const (

width = 800

height = 600

WORD_WIDTH float32 = 1500

WORD_HEIGHT float32 = 1000

)

var (

windowName = "æˆ‘çˆ±ä½ "

game2D = game.NewGame(width,height,WORD_WIDTH,WORD_HEIGHT)

deltaTime = 0.0

lastFrame = 0.0

)

func main(){

runtime.LockOSThread()

window := initGlfw()

defer glfw.Terminate()

initOpenGL()

game2D.Init()

for !window.ShouldClose() {

currentFrame := glfw.GetTime();

deltaTime = currentFrame - lastFrame;

lastFrame = currentFrame;

glfw.PollEvents()

game2D.ProcessInput(deltaTime)

game2D.Update(deltaTime)

gl.Clear(gl.COLOR_BUFFER_BIT);

game2D.Render(deltaTime)

window.SwapBuffers()

}

}

func initGlfw() *glfw.Window {

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img



既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Python开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注Python)
img

一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。

img
img

二、Python必备开发工具

工具都帮大家整理好了,安装就可直接上手!img

三、最新Python学习笔记

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。

img

四、Python视频合集

观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

img

五、实战案例

纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。img

六、面试宝典

在这里插入图片描述

在这里插入图片描述

简历模板在这里插入图片描述

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

学习。img

六、面试宝典

在这里插入图片描述

在这里插入图片描述

简历模板在这里插入图片描述

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
个人学习golang笔记,从各种教程中总结而来,作为入门参考。目录如下 目录 1. 入门 1 1.1. Hello world 1 1.2. 命令行参数 2 2. 程序结构 3 2.1. 类型 4 2.1.1. 命名类型(named type)与未命名类型(unamed type) 4 2.1.2. 基础类型(underlying type) 4 2.1.3. 可赋值性 5 2.1.4. 类型方法集 6 2.1.5. 类型声明 6 2.2. 变量 8 2.2.1. 变量声明 8 2.2.2. 类型零值 12 2.2.3. 指针 13 2.3. 赋值 17 2.4. 包和文件 17 2.5. 作用域 18 2.6. 语句 19 2.7. 比较运算符 20 2.8. 类型转换 21 2.9. 控制流 23 2.9.1. If 23 2.9.2. Goto 24 2.9.3. For 25 2.9.4. Switch 25 2.9.5. break语句 31 2.9.6. Continue语句 31 3. 基础数据类型 31 3.1. golang类型 31 3.2. Numeric types 32 3.3. 字符串 33 3.3.1. 什么是字符串 33 3.3.2. 字符串底层概念 35 3.3.3. 获取每个字节 38 3.3.4. Rune 39 3.3.5. 字符串的 for range 循环 40 3.3.6. 用字节切片构造字符串 41 3.3.7. 用rune切片构造字符串 42 3.3.8. 字符串的长度 42 3.3.9. 字符串是不可变的 42 3.3.10. UTF8(go圣经) 43 3.4. 常量 45 3.4.1. 常量定义 45 3.4.2. 常量类型 46 3.4.3. Iota 46 4. 组合数据类型 47 4.1. 数组 47 4.1.1. 数组概述 47 4.1.2. 数组的声明 49 4.1.3. 数组的长度 50 4.1.4. 遍历数组 50 4.1.5. 多维数组 51 4.2. 切片 52 4.2.1. 什么是切片 52 4.2.2. 切片概述 55 4.2.3. 创建一个切片 55 4.2.4. 切片遍历 57 4.2.5. 切片的修改 58 4.2.6. 切片的长度和容量 60 4.2.7. 追加切片元素 62 4.2.8. 切片的函数传递 65 4.2.9. 多维切片 66 4.2.10. 内存优化 67 4.2.11. nil slice和empty slice 69 4.2.12. For range 70 4.3. 结构 71 4.3.1. 什么是结构体? 71 4.3.2. 结构体声明 73 4.3.3. 结构体初始化 77 4.3.4. 嵌套结构体(Nested Structs) 81 4.3.5. 匿名字段 82 4.3.6. 导出结构体和字段 84 4.3.7. 结构体相等性(Structs Equality) 85 4.4. 指针类型 86 4.5. 函数 87 4.6. map 87 4.6.1. 什么是map 87 4.6.2. 声明、初始化和make 89 4.6.3. 给 map 添加元素 91 4.6.4. 获取 map 中的元素 91 4.6.5. 删除 map 中的元素 92 4.6.6. 获取 map 的长度 92 4.6.7. Map 的相等性 92 4.6.8. map的排序 92 4.7. 接口 93 4.7.1. 什么是接口? 93 4.7.2. 接口的声明与实现 96 4.7.3. 接口的实际用途 97 4.7.4. 接口的内部表示 99 4.7.5. 空接口 102 4.7.6. 类型断言 105 4.7.7. 类型选择(Type Switch) 109 4.7.8. 实现接口:指针接受者与值接受者 112 4.7.9. 实现多个接口 114 4.7.10. 接口的嵌套 116 4.7.11. 接口的零值 119 4.8. Channel 120 4.9. 类型转换 120 5. 函数 120 5.1. 函数的声明 121 5.2. 一个递归函数的例子( recursive functions) 121 5.3. 多返回值 121 5.4. 命名返回值 121 5.5. 可变函数参数 122 5.6. Defer 123 5.6.1. Defer语句介绍 123 5.6.2. Defer使用场景 128 5.7. 什么是头等(第一类)函数? 130 5.8. 匿名函数 130 5.9. 用户自定义的函数类型 132 5.10. 高阶函数(装饰器?) 133 5.10.1. 把函数作为参数,传递给其它函数 134 5.10.2. 在其它函数中返回函数 134 5.11. 闭包 135 5.12. 头等函数的实际用途 137 6. 微服务创建 140 6.1. 使用net/http创建简单的web server 140 6.2. 读写JSON 144 6.2.1. Marshal go结构到JSON 144 6.2.2. Unmarshalling JSON 到Go结构 146 7. 方法 146 7.1. 什么是方法? 146 7.2. 方法示例 146 7.3. 函数和方法区别 148 7.4. 指针接收器与值接收器 153 7.5. 那么什么时候使用指针接收器,什么时候使用值接收器? 155 7.6. 匿名字段的方法 156 7.7. 在方法中使用值接收器 与 在函数中使用值参数 157 7.8. 在方法中使用指针接收器 与 在函数中使用指针参数 159 7.9. 在非结构体上的方法 161 8. 并发入门 162 8.1. 并发是什么? 162 8.2. 并行是什么? 162 8.3. 从技术上看并发和并行 163 8.4. Go 对并发的支持 164 9. Go 协程 164 9.1. Go 协程是什么? 164 9.2. Go 协程相比于线程的优势 164 9.3. 如何启动一个 Go 协程? 165 9.4. 启动多个 Go 协程 167 10. 信道channel 169 10.1. 什么是信道? 169 10.2. 信道的声明 169 10.3. 通过信道进行发送和接收 169 10.4. 发送与接收默认是阻塞的 170 10.5. 信道的代码示例 170 10.6. 信道的另一个示例 173 10.7. 死锁 174 10.8. 单向信道 175 10.9. 关闭信道和使用 for range 遍历信道 176 11. 缓冲信道和工作池(Buffered Channels and Worker Pools) 179 11.1. 什么是缓冲信道? 179 11.2. 死锁 182 11.3. 长度 vs 容量 183 11.4. WaitGroup 184 11.5. 工作池的实现 186 12. Select 188 12.1. 什么是 select? 188 12.2. 示例 189 12.3. select 的应用 190 12.4. 默认情况 190 12.5. 死锁与默认情况 191 12.6. 随机选取 191 12.7. 这下我懂了:空 select 191 13. 文件读写 191 13.1. GoLang几种读文件方式的比较 197 14. 个人 197 14.1. ++,-- 198 14.2. 逗号 198 14.3. 未使用的变量 199 14.4. Effective go 199 14.4.1. 指针 vs. 值 199 14.5. 可寻址性-map和slice的区别 201 14.5.1. slice 201 14.5.2. map 202 14.6. golang库 203 14.6.1. unicode/utf8包 203 14.6.2. time包 205 14.6.3. Strings包 205 14.6.4. 输入输出 212 14.6.5. 正则处理 224 14.6.6. Golang内建函数 226

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值