はじめに
ゲームを作ろうと思ったら、やっぱりキャラクターに動きは必要ですよね。
とりあえず歩くモーションだけでも作りたいですよね。
私は作りたいので、作ってみました。
サンプルコードの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)
}
中段の画像とは、赤で囲んだ部分のことです。
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)
}
}
完成
次は十字キーで動かしたいと思います。