Skip to content

Commit 20ee4a6

Browse files
authored
Merge pull request #25 from Senoue/feature/fix-goradius
Fix goradius
2 parents 7b650e4 + 587414e commit 20ee4a6

7 files changed

Lines changed: 330 additions & 1 deletion

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ smoketest: FORCE
1212
tinygo build -o ./out/all.gopher-board-i2c.uf2 --size short --target ./targets/gopher-board-i2c.json ./games/all/
1313
tinygo build -o ./out/all.gopher-board-spi.uf2 --size short --target ./targets/gopher-board-spi.json ./games/all/
1414
tinygo build -o ./out/all.wasm --size short --target wasm --no-debug --panic trap ./games/all/
15-
15+
tinygo build -o ./out/goradius.zero-kb02.uf2 --size short --target ./targets/zero-kb02.json ./games/goradius/
1616

1717
FORCE:

games/all/all/all.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/sago35/koebiten"
77
"github.com/sago35/koebiten/games/blocks/blocks"
88
"github.com/sago35/koebiten/games/flappygopher/flappygopher"
9+
"github.com/sago35/koebiten/games/goradius/goradius"
910
"github.com/sago35/koebiten/games/jumpingopher/jumpingopher"
1011
"github.com/sago35/koebiten/games/snakegame/snakegame"
1112
)
@@ -66,6 +67,16 @@ func NewGame() *Menu {
6667
}
6768
},
6869
},
70+
{
71+
Title: "Goradius",
72+
Game: func() {
73+
koebiten.SetRotation(koebiten.Rotation0)
74+
game := goradius.NewGame()
75+
if err := koebiten.RunGame(game); err != nil {
76+
log.Fatal(err)
77+
}
78+
},
79+
},
6980
})
7081

7182
return menu

games/goradius/goradius/beam.png

1.89 KB
Loading

games/goradius/goradius/enemy.png

2.28 KB
Loading

games/goradius/goradius/game.go

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
package goradius
2+
3+
import (
4+
"embed"
5+
"math/rand/v2"
6+
"slices"
7+
8+
"github.com/sago35/koebiten"
9+
"tinygo.org/x/drivers/pixel"
10+
)
11+
12+
//go:embed *.png
13+
var fsys embed.FS
14+
15+
const (
16+
gameStateStart = iota
17+
gameStatePlaying
18+
gameStateGameOver
19+
20+
width = 128
21+
height = 64
22+
23+
gopherWidth = 20
24+
gopherHeight = 25
25+
26+
beamMax = 1
27+
beamCooldown = 60
28+
)
29+
30+
type Game struct {
31+
gopher *koebiten.Image
32+
x, y int
33+
scale float32
34+
theta float32
35+
score int
36+
beamEnergy int
37+
beamActive bool // ビームが有効かどうかのフラグ
38+
beamCooldownTimer int // ビームのクールダウンタイマー
39+
40+
gameState int // ゲームの状態を管理する変数
41+
}
42+
43+
type enemy struct {
44+
enemyX int
45+
enemyY int
46+
speed int // 敵の移動速度
47+
}
48+
49+
var (
50+
white = pixel.NewMonochrome(0xFF, 0xFF, 0xFF)
51+
black = pixel.NewMonochrome(0x00, 0x00, 0x00)
52+
53+
x = float32(20.0)
54+
y = float32(30.0)
55+
56+
frames = 30 // フレーム数
57+
interval = 120 // 敵の追加間隔
58+
enemie = []*enemy{} // 敵のX座標とY座標
59+
)
60+
61+
func NewGame() *Game {
62+
game := &Game{
63+
gopher: koebiten.NewImageFromFS(fsys, "gopher.png"),
64+
x: width / 2,
65+
y: height / 2,
66+
scale: 1,
67+
beamEnergy: 1, // 初期エネルギーを1に設定
68+
}
69+
return game
70+
}
71+
72+
// Game update process
73+
func (g *Game) Update() error {
74+
ds := float32(0.05)
75+
dt := float32(0.2)
76+
speed := 1
77+
dx := 1 * speed
78+
dy := 1 * speed
79+
80+
// スタート画面からゲームプレイ画面に遷移
81+
if koebiten.IsKeyPressed(koebiten.Key0) {
82+
// スコアのリセット
83+
g.score = 0
84+
g.gameState = gameStatePlaying
85+
}
86+
87+
// rotary buttonを回すとgopherが回転する
88+
// キーボードを押しながら回すと拡大縮小する
89+
if koebiten.IsKeyPressed(koebiten.KeyRotaryRight) {
90+
if isAnyKeyboardKeyPressed() {
91+
g.scale += ds
92+
} else {
93+
g.theta += dt
94+
}
95+
}
96+
if koebiten.IsKeyPressed(koebiten.KeyRotaryLeft) {
97+
if isAnyKeyboardKeyPressed() {
98+
g.scale -= ds
99+
} else {
100+
g.theta -= dt
101+
}
102+
}
103+
104+
// joystickを倒すとgopherが移動する
105+
if koebiten.IsKeyPressed(koebiten.KeyArrowRight) {
106+
if g.x < width {
107+
g.x += dx
108+
}
109+
}
110+
if koebiten.IsKeyPressed(koebiten.KeyArrowLeft) {
111+
if g.x > -5 {
112+
g.x -= dx
113+
}
114+
}
115+
if koebiten.IsKeyPressed(koebiten.KeyArrowDown) {
116+
if g.y <= height {
117+
g.y += dy
118+
}
119+
}
120+
if koebiten.IsKeyPressed(koebiten.KeyArrowUp) {
121+
if g.y > -5 {
122+
g.y -= dy
123+
}
124+
}
125+
126+
// ビームを発射する
127+
if g.beamCooldownTimer > 0 {
128+
// クールダウン中はタイマーを減らす
129+
g.beamCooldownTimer--
130+
g.beamActive = false
131+
// クールダウンが終わったらエネルギーを1に回復
132+
if g.beamCooldownTimer == 0 {
133+
g.beamEnergy = 1
134+
}
135+
} else if koebiten.IsKeyPressed(koebiten.Key1) && g.beamEnergy > 0 {
136+
// エネルギーがある場合のみビーム発射
137+
g.beamActive = true
138+
g.beamEnergy--
139+
// エネルギーが0になったらクールダウン開始
140+
if g.beamEnergy <= 0 {
141+
g.beamCooldownTimer = beamCooldown
142+
}
143+
} else {
144+
// Key1が押されていない場合またはエネルギーがない場合はビームを無効化
145+
g.beamActive = false
146+
}
147+
148+
// 敵の移動
149+
for i := 0; i < len(enemie); i++ {
150+
if i < len(enemie) { // 境界チェック
151+
enemie[i].enemyX -= 1 // 少しずつ左へ
152+
153+
// 画面外に出た敵を削除
154+
if enemie[i].enemyX < -gopherWidth {
155+
enemie = append(enemie[:i], enemie[i+1:]...)
156+
i--
157+
}
158+
}
159+
}
160+
161+
return nil
162+
}
163+
164+
func (g *Game) drawEnemy(e *enemy) {
165+
// 敵を描画する
166+
koebiten.DrawImageFS(nil, fsys, "enemy.png", e.enemyX, e.enemyY)
167+
// 自機との当たり判定
168+
if hitRects(g.x, g.y, g.x+gopherWidth, g.y+gopherHeight,
169+
e.enemyX, e.enemyY, e.enemyX+gopherWidth, e.enemyY+gopherHeight) {
170+
// 当たった場合、ゲームオーバー
171+
g.gameState = gameStateGameOver
172+
}
173+
}
174+
175+
func (g *Game) drawTitle() {
176+
// タイトル画面を描画する
177+
koebiten.Println("Goradius")
178+
koebiten.Println("Press key to start")
179+
}
180+
181+
func (g *Game) drawGameOver() {
182+
// スコアを表示する
183+
koebiten.Println("Score:", g.score)
184+
// ゲームオーバー画面を描画する
185+
koebiten.Println("Game Over")
186+
}
187+
188+
func (g *Game) drawGame(screen *koebiten.Image) {
189+
// 自機描画
190+
op := koebiten.DrawImageOptions{}
191+
op.GeoM.Translate(-float32(gopherWidth)/2, -float32(gopherHeight)/2)
192+
op.GeoM.Scale(g.scale, g.scale)
193+
op.GeoM.Rotate(g.theta)
194+
op.GeoM.Translate(float32(g.x), float32(g.y))
195+
g.gopher.DrawImage(screen, op)
196+
197+
// スコアとエネルギー表示する
198+
koebiten.Println("beam:", g.beamEnergy)
199+
koebiten.Println("Score:", g.score)
200+
201+
// 一定間隔で敵を追加
202+
frames++
203+
if rand.N(interval) < 3 {
204+
enemyY := rand.N(height - gopherHeight)
205+
// スピードもランダムに(1〜3の範囲)
206+
enemySpeed := rand.N(3) + 1
207+
enemy := &enemy{width, enemyY, enemySpeed}
208+
enemie = append(enemie, enemy)
209+
}
210+
211+
// 敵の描画
212+
for i := 0; i < len(enemie); i++ {
213+
if i < len(enemie) { // 境界チェック
214+
g.drawEnemy(enemie[i])
215+
}
216+
}
217+
218+
// ビームを描画(ビームが有効な場合)
219+
if g.beamActive && g.beamEnergy <= beamMax {
220+
for i := 0; i < 10; i++ {
221+
beamX := g.x + (i * 10)
222+
beamY := g.y
223+
koebiten.DrawImageFS(nil, fsys, "beam.png", beamX, beamY)
224+
225+
// 各ビームセグメントと敵の当たり判定
226+
for j := 0; j < len(enemie); j++ {
227+
if j < len(enemie) { // 配列の境界チェック
228+
if hitBeam(beamX, beamY, beamX+gopherWidth, beamY+gopherHeight,
229+
enemie[j].enemyX, enemie[j].enemyY, enemie[j].enemyX+gopherWidth, enemie[j].enemyY+gopherHeight) {
230+
231+
// 敵が当たった場合、敵を削除して得点加算
232+
g.score++
233+
234+
// 敵をスライスから削除
235+
enemie = append(enemie[:j], enemie[j+1:]...)
236+
j-- // 削除したので、インデックスを1つ戻す
237+
break
238+
}
239+
}
240+
}
241+
}
242+
}
243+
}
244+
245+
// Screen size
246+
func (g *Game) Layout(outsideWidth, outsideHeight int) (w, h int) {
247+
return width, height
248+
}
249+
250+
func (g *Game) Draw(screen *koebiten.Image) {
251+
switch g.gameState {
252+
case gameStateStart:
253+
g.drawTitle()
254+
case gameStatePlaying:
255+
g.drawGame(screen)
256+
case gameStateGameOver:
257+
g.drawGameOver()
258+
default:
259+
g.drawTitle()
260+
}
261+
}
262+
263+
// isAnyKeyboardKeyPressed returns true if any keyboard key is pressed
264+
//
265+
// keyboard key are koebiten.Key0 to koebiten.Key11
266+
func isAnyKeyboardKeyPressed() bool {
267+
return slices.ContainsFunc(koebiten.AppendPressedKeys(nil), func(k koebiten.Key) bool {
268+
switch k {
269+
case
270+
koebiten.Key0,
271+
koebiten.Key1,
272+
koebiten.Key2,
273+
koebiten.Key3,
274+
koebiten.Key4,
275+
koebiten.Key5,
276+
koebiten.Key6,
277+
koebiten.Key7,
278+
koebiten.Key8,
279+
koebiten.Key9,
280+
koebiten.Key10,
281+
koebiten.Key11:
282+
return true
283+
default:
284+
return false
285+
}
286+
})
287+
}
288+
289+
// キャラと敵の当たり判定で、場所が重なっているかどうかを判定する
290+
func hitRects(aLeft, aTop, aRight, aBottom, bLeft, bTop, bRight, bBottom int) bool {
291+
return aTop < bBottom && bTop < aBottom && aLeft < bRight && bLeft < aRight
292+
}
293+
294+
func hitBeam(aLeft, aTop, aRight, aBottom, bLeft, bTop, bRight, bBottom int) bool {
295+
return aTop < bBottom && bTop < aBottom && aLeft < bRight && bLeft < aRight
296+
}

games/goradius/goradius/gopher.png

896 Bytes
Loading

games/goradius/main.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package main
2+
3+
import (
4+
"log"
5+
6+
"github.com/sago35/koebiten"
7+
8+
"github.com/sago35/koebiten/games/goradius/goradius"
9+
"github.com/sago35/koebiten/hardware"
10+
)
11+
12+
func main() {
13+
koebiten.SetHardware(hardware.Device)
14+
koebiten.SetWindowSize(128, 64)
15+
koebiten.SetWindowTitle("GeoM Gopher")
16+
17+
game := goradius.NewGame()
18+
19+
if err := koebiten.RunGame(game); err != nil {
20+
log.Fatal(err)
21+
}
22+
}

0 commit comments

Comments
 (0)