4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ebitenでキャラクターが歩くモーションを作ってみる

Last updated at Posted at 2021-07-16

はじめに

ゲームを作ろうと思ったら、やっぱりキャラクターに動きは必要ですよね。
とりあえず歩くモーションだけでも作りたいですよね。
私は作りたいので、作ってみました。

サンプルコードのanimationを見てみる

ebitenインストール→サンプルのanimationをみる→めっちゃヌルヌル動くやん。。。
まずはこのサンプルを解析しましょう。

必要なところだけ抽出して解説します。

const (
	screenWidth  = 320
	screenHeight = 240

	frameOX     = 0			// フレーム開始時点のX座標
	frameOY     = 32		// フレーム開始時点のY座標
	frameWidth  = 32		// 1フレームで表示する横幅
	frameHeight = 32		// 1フレームで表示する縦幅
	frameNum    = 8			// 表示させる画像の数
)

var (
	// イメージの宣言(今回省略したmain関数の中で画像自体の読み込みをしています)
	runnerImage *ebiten.Image
)

type Game struct {
	count int
}

func (g *Game) Update() error {
	// マイフレーム毎にgを増加させていく
	g.count++
	return nil
}

func (g *Game) Draw(screen *ebiten.Image) {
	// 画像のオプションを宣言
	op := &ebiten.DrawImageOptions{}

	// 画像をスクリーンの中心点へ移動
	op.GeoM.Translate(-float64(frameWidth)/2, -float64(frameHeight)/2)
	op.GeoM.Translate(screenWidth/2, screenHeight/2)

	// 5フレーム毎に1つの絵が切り替わるようにする
	// もし 5 ではなく60 で割った時、 g.count / 60 が 1になるのは 1秒後(60FPS)
	// 1 % frameNum は 1となる
	// 2秒後には 120 / 60 となるので、 2 % 8 = 2
	// つまりここの数字は何フレーム経過後に変数iが変わっていくか?を制御している
	i := (g.count / 5) % frameNum

	// sx は基準となるframeOX(=0)にframeWidth(=32)をiの数だけ足したもの
	// つまりsxはi*32の数になり、基準点が右に動いていく
	// sy は常にframeOY(=32)
	sx, sy := frameOX+i*frameWidth, frameOY

	// 表示させる画像を切り出す
	// Xの範囲は sx から sx+frameWidth
	// Yの範囲は常に sy(=32)
	// つまり、画像の中段を、5フレーム毎に右にズレながら切り出していく
	var this_frame_img *ebiten.Image = runnerImage.SubImage(image.Rect(sx, sy, sx+frameWidth, sy+frameHeight)).(*ebiten.Image)

	// 画像を表示させる
	screen.DrawImage(this_frame_img, op)
}

中段の画像とは、赤で囲んだ部分のことです。
Image from Gyazo
5フレーム毎に32ピクセルずつ右にズレていきながら抜き出して表示しているため、あんな風にヌルヌル動いていたんですね。

歩行モーションを作ってみる

画像の用意

兎にも角にも、画像が必要です。
ぴぽや倉庫さんの素材と「キャラクターなんとかJ」で作りました。

モーションについて

さて、基本的な歩行モーションの付け方はサンプルと同じなのですが、1つ違うのが
「右手→ニュートラル→左手→ニュートラル→右手→...」という動き方になる点です。
左から右にループさせるだけだと
「右手→ニュートラル→左手→右手→ニュートラル→左手→...」となってしまうので、ここを考慮しながら作ります。

完成したコード

前回の記事で作った画像表示のオリジナルパッケージ使って画像表示させてます

package main

import (
	"log"
	"image"
	_ "image/png"

	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/ebitenutil"
	"github.com/username/projectname/src/picture"  // 画像表示のオリジナルパッケージ
)

const (
	frameOX = 0				// フレーム開始時点のX座標
	frameOY = 0				// フレーム開始時点のY座標
	frameWidth = 32		// 1フレームで表示する横幅
	frameHeight = 32	// 1フレームで表示する縦幅
	frameNum = 4			// 表示させる画像の数
)


var character *ebiten.Image

// 最初に画像を読み込む
func init() {
	var err error
	character, _, err = ebitenutil.NewImageFromFile("character.png")
	if err != nil {
		log.Fatal(err)
	}
}

type Game struct{
	count int
}

func (g *Game) Update() error{
	// 毎フレーム毎にgを増加させていく
	g.count++
	return nil
}

func(g *Game) Draw(screen *ebiten.Image){
	// 13フレームに一度画像を更新する
	i := (g.count / 13) % frameNum

	// i が3 すなわち右手→ニュートラル→左手→ここ の時にニュートラルを表示させる
	if i == 3 {
		i = 1
	}
	
	sx, sy := frameOX + i * frameWidth, frameOY

	// 表示する画像を切り出す
	var this_frame_img *ebiten.Image = character.SubImage(image.Rect(sx, sy, sx + frameWidth, sy + frameHeight)).(*ebiten.Image)

	// 画像を表示させる
	picture.Show(screen, this_frame_img, 1, 100,100,0)
}



func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
	return 320, 240
}

func main() {
	ebiten.SetWindowSize(640, 480)
	ebiten.SetWindowTitle("walk")
	if err := ebiten.RunGame(&Game{}); err != nil {
		log.Fatal(err)
	}
}

完成

Image from Gyazo

次は十字キーで動かしたいと思います。

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?