ebiten 实现角色移动

ebiten 实现角色移动


构思如何实现角色移动

  • 总所周知,游戏中的角色移动依靠的是图片的变动,在快速变换的情况下,达到人物移动的效果.类似于翻书动画
  • 游戏开发中常用到的图片一般是精灵图,比如将人物的动作放在同一图片的图片,不了解精灵图的可以自行了解一下
  • 介绍到这里,开始在go中实现角色移动
    • 1.定义角色的结构体(精灵图,宽度,高度,移动速度…)
    • 2.定义角色移动图片帧数的参数结构体(起点x,起点y,宽度,高度)
    • 3.将是参数输入到帧数参数数组
    • 4.实现角色行走动作,实现角色真实移动逻辑代码
    • 5.实现角色行走动作,实现角色真实移动渲染代码
    • 6.初始化数据

素材

跑步动作
休闲动作

效果展示


1.定义角色的结构体

type Player struct{
    
    
    image *ebiten.Image //精灵图
    x float64 //人物生成横坐标
    y float64 //人物生成纵坐标
    speed float64  //人物移动速度
}
type Game struct{
    
    
    role *Player
    frameCounter int
}

2.定义角色移动图片帧数的参数结构体

type Frame struct{
    
    
    x int//起点x
    y int //起点y
    width int //宽度
    height int //高度
}

3.将参数输入到帧数参数数组

  • 如何测量帧数图片的参数:
    • 我自己是打开ps一点一点测量的,不知道各位有什么更好的方法
// 跑步定义动画帧
var frames = []Frame{
    
    
    {
    
    21, 34, 25, 30},
    {
    
    88, 34, 25, 30},
    {
    
    151, 34, 25, 30},
    {
    
    213, 34, 25, 30},
    {
    
    280, 34, 25, 30},
    {
    
    343, 34, 25, 30},
}

4.实现角色行走动作,实现角色真实移动逻辑代码

  • 本人觉得人物的动作和行走的关系类似于,一个相框和图片的关系,相框移动代表角色移动,图片更换代表角色的动作
  • 这里我将动画帧函数和更新角色位置分开写进Update函数当中
  • 此处的g.frameCounter++,animationCounter++用于减缓角色移动的速度以及动画更新的速度,不添加的情况下,角色会很鬼畜以及移动很快,对应数值越大就越慢,这个就类似一定时器一般,只有达到条件才能执行代码
  • 参数介绍
    • fromTurn 用于跑动作和其他动作的交换
    • moveSpeed 是帧数数组的下标
  • 当检测到有左右和上按键时,就增加一次下标,要注意不要越界访问
  • 当检测到下按键时,就减少一次下标,实现倒退的感觉

  • 角色移动就是普通的增加和减少坐标实现角色移动
  • 注意:上下的控键要分开写,直接实现可以往斜方向走
func (g *Game) Update() error {
    
    
	g.frameCounter++
	animationCounter++
	// 更新动画帧
	if animationCounter >= 4 {
    
    
		animationCounter = 0//及时置零
		if ebiten.IsKeyPressed(ebiten.KeyRight) || ebiten.IsKeyPressed(ebiten.KeyLeft) || ebiten.IsKeyPressed(ebiten.KeyUp) {
    
    
			fromTurn = 1
			moveSpeed += 1
                        //防越界访问
			if moveSpeed == 6 {
    
    
				moveSpeed = 0
			}
		} else if ebiten.IsKeyPressed(ebiten.KeyDown) {
    
    
			fromTurn = 1
			moveSpeed -= 1
			if moveSpeed == -1 {
    
    
				moveSpeed = 5
			}
		} else {
    
    
			fromTurn = 0
		}
	}
	// 更新角色位置
	if g.frameCounter >= 2 {
    
    
		g.frameCounter = 0
		if ebiten.IsKeyPressed(ebiten.KeyRight) {
    
    
			g.role.x += g.role.speed
		} else if ebiten.IsKeyPressed(ebiten.KeyLeft) {
    
    
			g.role.x -= g.role.speed
		} else {
    
    
			g.role.x += 0
		}

		if ebiten.IsKeyPressed(ebiten.KeyUp) {
    
    
			g.role.y -= g.role.speed
		} else if ebiten.IsKeyPressed(ebiten.KeyDown) {
    
    
			g.role.y += g.role.speed
		} else {
    
    
			g.role.y += 0
		}
	}
    return nil
}

5. 实现角色行走动作,实现角色真实移动渲染代码

  • ebitenutil.NewImageFromFile:获取精灵图,注意将图片路径更换
  • imgRun.SubImage()获取精灵图的子图
    • 参数: image.Rect(frame.X, frame.Y, frame.X+frame.Width, frame.Y+frame.Height)
    • 对应左上角和右下角坐标,达到只渲染精灵图的某一部分,最后强转为*ebiten.Image
  • optsRun := &ebiten.DrawImageOptions{}
    optsRun.GeoM.Translate(g.role.x, g.role.y)实现人物的移动,参数是人物的坐标
func (g *Game) drawRunning(screen *ebiten.Image) {
    
    
	// 跑步精灵图的绘制代码
	imgRun, _, errRun := ebitenutil.NewImageFromFile("G:\\Golang\\ebitenCode\\resource\\R2302082\\Pixel Crawler - FREE - 1.8\\Heroes\\Knight\\Run\\Run-Sheet.png")
	if errRun != nil {
    
    
		panic(errRun)
	}
        //获取当前的动画帧
	frame := frames[moveSpeed]
        //实现人物动作的渲染  
	subImageRun := imgRun.SubImage(image.Rect(frame.X, frame.Y, frame.X+frame.Width, frame.Y+frame.Height)).(*ebiten.Image)
        //实现人物移动
	optsRun := &ebiten.DrawImageOptions{
    
    }
	optsRun.GeoM.Translate(g.role.x, g.role.y)
        //渲染
	screen.DrawImage(subImageRun, optsRun)
}

6.初始化数据

func NewGame() *Game {
    
    
	return &Game{
    
    
		role: &Player{
    
    nil, 100, 100, 3},
	}
}

注意:

1.在需要将图片地址换成你本机的相对地址或绝对地址(我相对老是添加不对,求各位教教)
2.完整代码中包含另外一套动作,希望各位自己动手利用顶部两张图片实现代码

完整代码

package main

import (
	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/ebitenutil"
	"image"
)


// Player 角色定义
type Player struct {
    
    
	image *ebiten.Image // 精灵图
	x     float64       // 精灵图横坐标
	y     float64       // 精灵图纵坐标
	speed float64       // 精灵图移动速度
}

type Frame struct {
    
    
	X, Y, Width, Height int
}

// 跑步定义动画帧
var frames = []Frame{
    
    
	{
    
    21, 34, 25, 30},
	{
    
    88, 34, 25, 30},
	{
    
    151, 34, 25, 30},
	{
    
    213, 34, 25, 30},
	{
    
    280, 34, 25, 30},
	{
    
    343, 34, 25, 30},
}

// IdIes 空闲动画
var IdIes = []Frame{
    
    
	{
    
    5, 2, 20, 30},
	{
    
    37, 2, 20, 30},
	{
    
    69, 2, 20, 30},
	{
    
    101, 2, 20, 30},
}

// 动画
var (
	moveSpeed = 0
	IdIeSpeed = 0
)
var fromTurn = 0

type Game struct {
    
    
	role         *Player
	frameCounter int
}

// (爱死)
// 添加一个新的变量来控制动画帧更新速度
var animationCounter = 0
var IdleCounter = 0

func (g *Game) Update() error {
    
    
	g.frameCounter++
	animationCounter++
	// 更新动画帧
	if animationCounter >= 4 {
    
    
		animationCounter = 0
		if ebiten.IsKeyPressed(ebiten.KeyRight) || ebiten.IsKeyPressed(ebiten.KeyLeft) || ebiten.IsKeyPressed(ebiten.KeyUp) {
    
    
			fromTurn = 1
			moveSpeed += 1
			if moveSpeed == 6 {
    
    
				moveSpeed = 0
			}
		} else if ebiten.IsKeyPressed(ebiten.KeyDown) {
    
    
			fromTurn = 1
			moveSpeed -= 1
			if moveSpeed == -1 {
    
    
				moveSpeed = 5
			}
		} else {
    
    
			fromTurn = 0
		}
	}
	// 更新角色位置
	if g.frameCounter >= 2 {
    
    
		g.frameCounter = 0
		if ebiten.IsKeyPressed(ebiten.KeyRight) {
    
    
			g.role.x += g.role.speed
		} else if ebiten.IsKeyPressed(ebiten.KeyLeft) {
    
    
			g.role.x -= g.role.speed
		} else {
    
    
			g.role.x += 0
		}

		if ebiten.IsKeyPressed(ebiten.KeyUp) {
    
    
			g.role.y -= g.role.speed
		} else if ebiten.IsKeyPressed(ebiten.KeyDown) {
    
    
			g.role.y += g.role.speed
		} else {
    
    
			g.role.y += 0
		}
	}
	//休闲动作
	if fromTurn == 0 {
    
    
		IdleCounter++
		if IdleCounter >= 9 {
    
    
			IdleCounter = 0
			IdIeSpeed += 1
			if IdIeSpeed == 4 {
    
    
				IdIeSpeed = 0
			}
		}
	}
	return nil
}

// 跑步和空闲
func (g *Game) drawRunning(screen *ebiten.Image) {
    
    
	// 跑步精灵图的绘制代码
	imgRun, _, errRun := ebitenutil.NewImageFromFile("G:\\Golang\\ebitenCode\\resource\\R2302082\\Pixel Crawler - FREE - 1.8\\Heroes\\Knight\\Run\\Run-Sheet.png")
	if errRun != nil {
    
    
		panic(errRun)
	}
	frame := frames[moveSpeed]
	subImageRun := imgRun.SubImage(image.Rect(frame.X, frame.Y, frame.X+frame.Width, frame.Y+frame.Height)).(*ebiten.Image)
	optsRun := &ebiten.DrawImageOptions{
    
    }
	optsRun.GeoM.Translate(g.role.x, g.role.y)
	screen.DrawImage(subImageRun, optsRun)
}

func (g *Game) drawIdle(screen *ebiten.Image) {
    
    
	// 空闲精灵图的绘制代码
	imgIdIe, _, errIdIe := ebitenutil.NewImageFromFile("G:\\Golang\\ebitenCode\\resource\\R2302082\\Pixel Crawler - FREE - 1.8\\Heroes\\Knight\\Idle\\Idle-Sheet.png")
	if errIdIe != nil {
    
    
		panic(errIdIe)
	}
	IdIe := IdIes[IdIeSpeed]
	subImageIdIe := imgIdIe.SubImage(image.Rect(IdIe.X, IdIe.Y, IdIe.Width+IdIe.X, IdIe.Height+IdIe.Y)).(*ebiten.Image)
	optsIdIe := &ebiten.DrawImageOptions{
    
    }
	optsIdIe.GeoM.Translate(g.role.x, g.role.y)
	screen.DrawImage(subImageIdIe, optsIdIe)
}

func (g *Game) Draw(screen *ebiten.Image) {
    
    
	if fromTurn == 1 {
    
    
		//跑步精灵图
		g.drawRunning(screen)
	} else if fromTurn == 0 {
    
    
		//空闲精灵图
		g.drawIdle(screen)
	}
}
func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
    
    
	return 640 * 2 / 3, 480 * 2 / 3
}
func NewGame() *Game {
    
    
	//img := ebiten.NewImage(25, 30)
	return &Game{
    
    
		role: &Player{
    
    nil, 100, 100, 3},
	}
}

func main() {
    
    
	ebiten.SetWindowTitle("实现角色移动")
	ebiten.SetWindowSize(640, 480)
	//启动游戏
	game := NewGame()
	if err := ebiten.RunGame(game); err != nil {
    
    
		panic(err)
	}
}

猜你喜欢

转载自blog.csdn.net/JUIU9527/article/details/131263988