制作一个简单的游戏,直接看代码注释:
game.go:
package engine
import (
SDL "github.com/veandco/go-sdl2/sdl"
)
const thickness = 15
const paddleH = 100.0
type Vector2 struct {
X float32
Y float32
}
type Game struct {
MWindow *SDL.Window
MRenderer *SDL.Renderer
MIsRunning bool
MPaddlePos Vector2
MBallPos Vector2
MBallVel Vector2
MPaddleDir int
MTicksCount uint32
}
//初始化引擎
func (g *Game) Initialize() bool {
var err error
//初始化SDL
if err = SDL.Init(SDL.INIT_VIDEO); err != nil {
SDL.Log("Unable to initialize SDL: %s", SDL.GetError())
return false
}
//创建窗口
if g.MWindow, err = SDL.CreateWindow(
"golang游戏引擎(电子游戏Pong)",
100,
100,
1024,
768,
0,
); err != nil {
SDL.Log("Failed to create window: %s", SDL.GetError())
return false
}
//创建渲染
if g.MRenderer, err = SDL.CreateRenderer(
g.MWindow,
-1,
SDL.RENDERER_ACCELERATED|SDL.RENDERER_PRESENTVSYNC,
); err != nil {
SDL.Log("Failed to create renderer: %s", SDL.GetError())
return false
}
//初始化参数
g.MPaddlePos.X = 10.0
g.MPaddlePos.Y = 768.0 / 2.0
g.MBallPos.X = 1024.0 / 2.0
g.MBallPos.Y = 768.0 / 2.0
g.MBallVel.X = -200.0
g.MBallVel.Y = 235.0
//保持运行
g.MIsRunning = true
return true
}
//引擎循环
func (g *Game) RunLoop() {
for {
g.ProcessInput()
g.UpdateGame()
g.GenerateOutput()
if !g.MIsRunning {
break
}
}
}
func (g *Game) ProcessInput() {
var event SDL.Event
for {
if event = SDL.PollEvent(); event == nil {
break
} else {
switch event.GetType() {
case SDL.QUIT: //窗口关闭退出
g.MIsRunning = false
}
}
}
//获取所有按键状态
state := SDL.GetKeyboardState()
//按下ESC退出
if state[SDL.SCANCODE_ESCAPE] == 1 {
g.MIsRunning = false
}
//检测按键WS
g.MPaddleDir = 0
if state[SDL.SCANCODE_W] == 1 {
g.MPaddleDir -= 1
}
if state[SDL.SCANCODE_S] == 1 {
g.MPaddleDir += 1
}
}
func (g *Game) UpdateGame() {
//等待
for {
//SDL_TICKS_PASSED(A, B)
if uint32(SDL.GetTicks64()) > g.MTicksCount+16 {
break
}
}
//增量时间
deltaTime := float32((uint32(SDL.GetTicks64()) - g.MTicksCount)) / 1000.0
if deltaTime > 0.05 {
deltaTime = 0.05
}
//总时间
g.MTicksCount = uint32(SDL.GetTicks64())
//更新球拍位置
if g.MPaddleDir != 0 {
g.MPaddlePos.Y += float32(g.MPaddleDir) * 300.0 * deltaTime
if g.MPaddlePos.Y < (paddleH/2.0 + thickness) {
g.MPaddlePos.Y = paddleH/2.0 + thickness
} else if g.MPaddlePos.Y > (768.0 - paddleH/2.0 - thickness) {
g.MPaddlePos.Y = 768.0 - paddleH/2.0 - thickness
}
}
//更新球位置
g.MBallPos.X += g.MBallVel.X * deltaTime
g.MBallPos.Y += g.MBallVel.Y * deltaTime
diff := g.MPaddlePos.Y - g.MBallPos.Y
if diff < 0 {
diff = -diff
}
//碰墙检测(左边离开屏幕和右边碰撞)
if diff <= paddleH/2.0 && g.MBallPos.X <= 25.0 && g.MBallPos.X >= 20.0 && g.MBallVel.X < 0.0 {
g.MBallVel.X *= -1.0
} else if g.MBallPos.X <= 0.0 {
g.MIsRunning = false
} else if g.MBallPos.X >= (1024.0-thickness) && g.MBallVel.X > 0.0 {
g.MBallVel.X *= -1.0
}
//碰墙检测(顶部和底部)
if g.MBallPos.Y <= thickness && g.MBallVel.Y < 0.0 {
g.MBallVel.Y *= -1
} else if g.MBallPos.Y >= (768-thickness) && g.MBallVel.Y > 0.0 {
g.MBallVel.Y *= -1
}
}
func (g *Game) GenerateOutput() {
//背景颜色
g.MRenderer.SetDrawColor(
0, // R
0, // G
255, // B
255, // A
)
//清屏
g.MRenderer.Clear()
//墙体颜色
g.MRenderer.SetDrawColor(
200, // R
200, // G
200, // B
255, // A
)
//顶部墙
wall := &SDL.Rect{
X: 0,
Y: 0,
W: 1024,
H: thickness,
}
g.MRenderer.FillRect(wall)
//底部墙
wall.Y = 768 - thickness
g.MRenderer.FillRect(wall)
//右边墙
wall.X = 1024 - thickness
wall.Y = 0
wall.W = thickness
wall.H = 1024
g.MRenderer.FillRect(wall)
//绘制球拍
paddle := &SDL.Rect{
X: int32(g.MPaddlePos.X),
Y: int32(g.MPaddlePos.Y - paddleH/2),
W: thickness,
H: int32(paddleH),
}
g.MRenderer.FillRect(paddle)
//绘制球
ball := &SDL.Rect{
X: int32(g.MBallPos.X),
Y: int32(g.MBallPos.Y),
W: thickness,
H: thickness,
}
g.MRenderer.FillRect(ball)
//交换缓冲区
g.MRenderer.Present()
}
//引擎销毁
func (g *Game) Shutdown() {
g.MRenderer.Destroy()
g.MWindow.Destroy()
SDL.Quit()
}