2D小游戏-创新设计——《弹射挑战》

时间:2025-02-16 08:40:50
package main import ( "encoding/json" "image/color" "log" "math" "math/rand" "os" "sort" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/ebitenutil" "github.com/hajimehoshi/ebiten/v2/inpututil" ) const ( screenWidth = 800 screenHeight = 600 gravity = 0.5 ) type Player struct { X, Y float64 Angle float64 Power float64 Score int Level int XP int Upgrades map[string]int Projectiles []*Projectile } type Projectile struct { X, Y float64 Vx, Vy float64 Active bool } type Target struct { X, Y float64 Value int } type Game struct { Player *Player Targets []*Target CurrentLevel int Launched bool Ranking []ScoreEntry ShowRanking bool UpgradeChoices []string } type ScoreEntry struct { Name string Score int Level int } func (g *Game) SaveRanking() { file, _ := json.MarshalIndent(g.Ranking, "", " ") _ = os.WriteFile("ranking.json", file, 0644) } func (g *Game) LoadRanking() { if file, err := os.ReadFile("ranking.json"); err == nil { _ = json.Unmarshal(file, &g.Ranking) } } func (g *Game) AddScore(name string, score int, level int) { g.Ranking = append(g.Ranking, ScoreEntry{Name: name, Score: score, Level: level}) sort.Slice(g.Ranking, func(i, j int) bool { return g.Ranking[i].Score > g.Ranking[j].Score }) if len(g.Ranking) > 10 { g.Ranking = g.Ranking[:10] } } func NewGame() *Game { g := &Game{ Player: &Player{ X: 100, Y: screenHeight - 100, Upgrades: make(map[string]int), }, CurrentLevel: 1, } g.LoadRanking() g.GenerateLevel() return g } func (g *Game) GenerateLevel() { g.Targets = nil // 生成随机地形和目标 for i := 0; i < 3+g.CurrentLevel; i++ { g.Targets = append(g.Targets, &Target{ X: float64(400 + rand.Intn(300)), Y: float64(200 + rand.Intn(200)), Value: 100 * g.CurrentLevel, }) } } func (g *Game) fire() { p := g.Player radians := p.Angle * math.Pi / 180 power := p.Power * 5 proj := &Projectile{ X: p.X, Y: p.Y, Vx: math.Cos(radians) * power, Vy: math.Sin(radians) * power, Active: true, } p.Projectiles = append(p.Projectiles, proj) } func (g *Game) Update() error { p := g.Player // 升级选择 if len(g.UpgradeChoices) > 0 { if inpututil.IsKeyJustPressed(ebiten.Key1) { p.Upgrades[g.UpgradeChoices[0]]++ g.UpgradeChoices = nil } if inpututil.IsKeyJustPressed(ebiten.Key2) { p.Upgrades[g.UpgradeChoices[1]]++ g.UpgradeChoices = nil } return nil } // 控制逻辑 if ebiten.IsKeyPressed(ebiten.KeyLeft) { p.Angle-- } if ebiten.IsKeyPressed(ebiten.KeyRight) { p.Angle++ } if ebiten.IsKeyPressed(ebiten.KeyUp) { p.Power++ } if ebiten.IsKeyPressed(ebiten.KeyDown) { p.Power-- } if inpututil.IsKeyJustPressed(ebiten.KeySpace) && !g.Launched { g.fire() g.Launched = true } // 更新弹道 for _, proj := range p.Projectiles { if proj.Active { proj.Vy += gravity proj.X += proj.Vx proj.Y += proj.Vy // 碰撞检测 for _, t := range g.Targets { dx := proj.X - t.X dy := proj.Y - t.Y if dx*dx+dy*dy < 400 { p.Score += t.Value p.XP += t.Value / 10 t.Value = 0 } } if proj.Y > screenHeight-50 { proj.Active = false g.Launched = false } } } // 升级检查 if p.XP >= 1000 { p.Level++ p.XP -= 1000 g.UpgradeChoices = []string{"Power+", "MultiShot"} } return nil } func (g *Game) Draw(screen *ebiten.Image) { // 绘制玩家 ebitenutil.DrawCircle(screen, g.Player.X, g.Player.Y, 10, color.RGBA{0, 255, 0, 255}) // 绘制弹道线 radians := g.Player.Angle * math.Pi / 180 endX := g.Player.X + math.Cos(radians)*50 endY := g.Player.Y + math.Sin(radians)*50 ebitenutil.DrawLine(screen, g.Player.X, g.Player.Y, endX, endY, color.White) // 绘制目标和地形 for _, t := range g.Targets { if t.Value > 0 { ebitenutil.DrawCircle(screen, t.X, t.Y, 20, color.RGBA{255, 0, 0, 255}) } } // 绘制弹射物 for _, proj := range g.Player.Projectiles { if proj.Active { ebitenutil.DrawCircle(screen, proj.X, proj.Y, 5, color.RGBA{255, 255, 0, 255}) } } // 显示UI信息 ebitenutil.DebugPrint(screen, "Score: "+string(g.Player.Score)+ "\nLevel: "+string(g.Player.Level)+ "\nXP: "+string(g.Player.XP)+ "\nAngle: "+string(g.Player.Angle)+ "\nPower: "+string(g.Player.Power)) // 显示升级选项 if len(g.UpgradeChoices) > 0 { ebitenutil.DebugPrint(screen, "Choose Upgrade:\n1. "+g.UpgradeChoices[0]+ "\n2. "+g.UpgradeChoices[1]) } } func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { return screenWidth, screenHeight } func main() { ebiten.SetWindowSize(screenWidth, screenHeight) ebiten.SetWindowTitle("弹射挑战") if err := ebiten.RunGame(NewGame()); err != nil { log.Fatal(err) } }