从零开发游戏需要学习的c#模块,第二十章(2D 敌人与战斗触发)

本节课我们要学习的内容

  1. 在地图上随机生成红色敌人

  2. 玩家碰到敌人后,进入战斗模式

  3. 战斗胜利后敌人消失,获得分数

  4. 屏幕显示敌人数量

using Microsoft.Xna.Framework;

using Microsoft.Xna.Framework.Graphics;

using Microsoft.Xna.Framework.Input;

using System;

using System.Collections.Generic;

using System.IO;

using FontStashSharp;

namespace MY_FIRST_GAME

{

// ★ 游戏状态枚举

public enum GameState

{

Exploring, // 探索模式

Battling // 战斗模式

}

public class Game1 : Game

{

private GraphicsDeviceManager _graphics;

private SpriteBatch _spriteBatch;

// 玩家

private Texture2D playerTexture;

private Vector2 playerPosition;

private float playerSpeed = 200f;

private int playerHp = 100;

private int playerMaxHp = 100;

private int playerAttack = 15;

// 金币

private Texture2D coinTexture;

private List<Vector2> coins;

private Random rng;

private int score;

// ★ 敌人

private Texture2D enemyTexture;

private List<EnemyData> enemies;

// 字体

private SpriteFontBase font;

// ★ 游戏状态

private GameState state = GameState.Exploring;

// ★ 战斗相关

private EnemyData? currentEnemy;

private string battleMessage = "";

private float battleTimer = 0f;

private bool playerTurn = true;

public Game1()

{

_graphics = new GraphicsDeviceManager(this);

Content.RootDirectory = "Content";

IsMouseVisible = true;

}

protected override void Initialize()

{

_graphics.PreferredBackBufferWidth = 800;

_graphics.PreferredBackBufferHeight = 600;

_graphics.ApplyChanges();

playerPosition = new Vector2(400, 300);

rng = new Random();

coins = new List<Vector2>();

enemies = new List<EnemyData>();

score = 0;

SpawnCoins(5);

SpawnEnemies(3);

base.Initialize();

}

protected override void LoadContent()

{

_spriteBatch = new SpriteBatch(GraphicsDevice);

// 玩家图片

using var stream = File.OpenRead("Content/player.png");

playerTexture = Texture2D.FromStream(GraphicsDevice, stream);

// 金币纹理

coinTexture = new Texture2D(GraphicsDevice, 32, 32);

Color[] coinData = new Color[32 * 32];

for (int i = 0; i < coinData.Length; i++)

coinData[i] = Color.Gold;

coinTexture.SetData(coinData);

// ★ 敌人纹理(红色方块 48x48)

enemyTexture = new Texture2D(GraphicsDevice, 48, 48);

Color[] enemyData = new Color[48 * 48];

for (int i = 0; i < enemyData.Length; i++)

enemyData[i] = Color.Red;

enemyTexture.SetData(enemyData);

// 字体

var fontSystem = new FontSystem();

fontSystem.AddFont(File.ReadAllBytes("Content/consola.ttf"));

font = fontSystem.GetFont(18);

}

// ★ 敌人数据结构

private class EnemyData

{

public Vector2 Position;

public int Hp;

public int MaxHp;

public int Attack;

public bool IsAlive;

public string Name;

public EnemyData(Vector2 pos, string name, int hp, int attack)

{

Position = pos;

Name = name;

Hp = hp;

MaxHp = hp;

Attack = attack;

IsAlive = true;

}

}

private void SpawnCoins(int count)

{

for (int i = 0; i < count; i++)

{

float x = rng.Next(50, 750);

float y = rng.Next(50, 550);

coins.Add(new Vector2(x, y));

}

}

private void SpawnEnemies(int count)

{

for (int i = 0; i < count; i++)

{

float x = rng.Next(80, 720);

float y = rng.Next(80, 520);

enemies.Add(new EnemyData(

new Vector2(x, y),

"史莱姆" + (i + 1),

30 + rng.Next(0, 20),

8 + rng.Next(0, 6)

));

}

}

protected override void Update(GameTime gameTime)

{

KeyboardState keyboard = Keyboard.GetState();

float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

// ★ 根据游戏状态分发更新

if (state == GameState.Exploring)

UpdateExploring(keyboard, deltaTime);

else if (state == GameState.Battling)

UpdateBattling(keyboard, deltaTime);

if (keyboard.IsKeyDown(Keys.Escape))

Exit();

base.Update(gameTime);

}

// ★ 探索模式更新

private void UpdateExploring(KeyboardState keyboard, float deltaTime)

{

float speed = playerSpeed * deltaTime;

if (keyboard.IsKeyDown(Keys.W) || keyboard.IsKeyDown(Keys.Up))

playerPosition.Y -= speed;

if (keyboard.IsKeyDown(Keys.S) || keyboard.IsKeyDown(Keys.Down))

playerPosition.Y += speed;

if (keyboard.IsKeyDown(Keys.A) || keyboard.IsKeyDown(Keys.Left))

playerPosition.X -= speed;

if (keyboard.IsKeyDown(Keys.D) || keyboard.IsKeyDown(Keys.Right))

playerPosition.X += speed;

playerPosition.X = Math.Clamp(playerPosition.X, 32, 768);

playerPosition.Y = Math.Clamp(playerPosition.Y, 32, 568);

CheckCoinCollision();

CheckEnemyCollision();

if (coins.Count == 0) SpawnCoins(5);

if (enemies.Count == 0) SpawnEnemies(3);

}

// ★ 战斗模式更新

private void UpdateBattling(KeyboardState keyboard, float deltaTime)

{

if (currentEnemy == null || !currentEnemy.IsAlive)

{

EndBattle(true);

return;

}

if (playerHp <= 0)

{

EndBattle(false);

return;

}

// 按空格攻击

if (playerTurn && keyboard.IsKeyDown(Keys.Space))

{

// 玩家攻击

currentEnemy.Hp -= playerAttack;

battleMessage = $"你对 {currentEnemy.Name} 造成了 {playerAttack} 点伤害!";

playerTurn = false;

}

// 敌人回合(简单延迟)

if (!playerTurn)

{

battleTimer += deltaTime;

if (battleTimer >= 0.8f)

{

battleTimer = 0f;

// 敌人攻击

playerHp -= currentEnemy.Attack;

battleMessage = $"{currentEnemy.Name} 对你造成了 {currentEnemy.Attack} 点伤害!";

playerTurn = true;

}

}

}

// ★ 结束战斗

private void EndBattle(bool playerWon)

{

if (playerWon && currentEnemy != null)

{

score += 50;

currentEnemy.IsAlive = false;

enemies.Remove(currentEnemy);

}

currentEnemy = null;

battleMessage = "";

battleTimer = 0f;

playerTurn = true;

state = GameState.Exploring;

}

// ★ 敌人碰撞检测

private void CheckEnemyCollision()

{

Rectangle playerRect = new Rectangle(

(int)playerPosition.X - 32,

(int)playerPosition.Y - 32,

64, 64

);

for (int i = enemies.Count - 1; i >= 0; i--)

{

Rectangle enemyRect = new Rectangle(

(int)enemies[i].Position.X - 24,

(int)enemies[i].Position.Y - 24,

48, 48

);

if (playerRect.Intersects(enemyRect))

{

// 进入战斗

currentEnemy = enemies[i];

state = GameState.Battling;

battleMessage = $"遭遇了 {currentEnemy.Name}!按 空格 攻击";

playerTurn = true;

battleTimer = 0f;

break;

}

}

}

// 金币碰撞

private void CheckCoinCollision()

{

Rectangle playerRect = new Rectangle(

(int)playerPosition.X - 32,

(int)playerPosition.Y - 32,

64, 64

);

for (int i = coins.Count - 1; i >= 0; i--)

{

Rectangle coinRect = new Rectangle((int)coins[i].X, (int)coins[i].Y, 32, 32);

if (playerRect.Intersects(coinRect))

{

coins.RemoveAt(i);

score += 10;

}

}

}

protected override void Draw(GameTime gameTime)

{

GraphicsDevice.Clear(Color.CornflowerBlue);

_spriteBatch.Begin();

// 画金币

foreach (Vector2 coinPos in coins)

_spriteBatch.Draw(coinTexture, coinPos, Color.White);

// ★ 画敌人

foreach (EnemyData enemy in enemies)

{

_spriteBatch.Draw(

enemyTexture,

enemy.Position,

null,

Color.White,

0f,

new Vector2(24, 24),

1f,

SpriteEffects.None,

0f

);

}

// 画玩家

_spriteBatch.Draw(

playerTexture,

playerPosition,

null,

Color.White,

0f,

new Vector2(playerTexture.Width / 2, playerTexture.Height / 2),

1f,

SpriteEffects.None,

0f

);

// UI 文字

_spriteBatch.DrawString(font, $"分数:{score}", new Vector2(10, 10), Color.White);

_spriteBatch.DrawString(font, $"金币:{coins.Count}", new Vector2(10, 35), Color.Gold);

_spriteBatch.DrawString(font, $"敌人:{enemies.Count}", new Vector2(10, 60), Color.Red);

_spriteBatch.DrawString(font, $"HP:{playerHp}/{playerMaxHp}", new Vector2(10, 85), Color.LimeGreen);

// ★ 战斗界面

if (state == GameState.Battling && currentEnemy != null)

{

// 半透明背景

Texture2D overlay = new Texture2D(GraphicsDevice, 1, 1);

overlay.SetData(new[] { new Color(0, 0, 0, 180) });

_spriteBatch.Draw(overlay, new Rectangle(0, 250, 800, 100), Color.White);

_spriteBatch.DrawString(font, $"⚔️ {currentEnemy.Name}", new Vector2(20, 260), Color.Red);

_spriteBatch.DrawString(font, $"敌人HP:{currentEnemy.Hp}/{currentEnemy.MaxHp}", new Vector2(20, 285), Color.White);

_spriteBatch.DrawString(font, $"你的HP:{playerHp}/{playerMaxHp}", new Vector2(20, 310), Color.LimeGreen);

_spriteBatch.DrawString(font, battleMessage, new Vector2(20, 335), Color.Yellow);

}

_spriteBatch.DrawString(font, "WASD移动 | 靠近敌人战斗 | 战斗中按空格攻击", new Vector2(10, 570), Color.LightGray);

_spriteBatch.End();

base.Draw(gameTime);

}

}

}

🔑 核心新知识

1. 游戏状态机
cs 复制代码
public enum GameState { Exploring, Battling }

if (state == GameState.Exploring)
    UpdateExploring(...);
else if (state == GameState.Battling)
    UpdateBattling(...);

这就是你在设计模式课学的状态模式的实际应用。探索和战斗是两个完全独立的逻辑分支。

2. 战斗触发
cs 复制代码
if (playerRect.Intersects(enemyRect))
{
    currentEnemy = enemies[i];
    state = GameState.Battling;
}
. 战斗流程
  • 玩家回合:按空格键攻击

  • 敌人回合:等待 0.8 秒后自动反击

  • 战斗结束:敌人死亡或玩家死亡,回到探索模式

相关推荐
gubilei7 小时前
网络安全学习之综合知识点
学习
咸甜适中7 小时前
rust语言学习笔记Trait(九)PartialEq、 Eq(相等比较)
笔记·学习·rust
searchforAI7 小时前
视频画面里的PPT怎么提取?视频转图文讲义的实操教程
人工智能·学习·ai·aigc·powerpoint·音视频·贴图
nashane7 小时前
HarmonyOS 6学习:文件打开方式应用重复的根治方案与最佳实践
学习·华为·harmonyos
解局易否结局7 小时前
从零上手 ops-transformer:一个有清晰路径感的学习计划
深度学习·学习·transformer
结衣结衣.8 小时前
走进机器学习:新手必看的完整入门指南
人工智能·python·学习·机器学习
我是唐青枫8 小时前
C#.NET YARP + OpenTelemetry:网关链路追踪实战
开发语言·c#·.net
xian_wwq8 小时前
【学习笔记】探讨大模型应用安全建设系列5——供应链安全与数据防护
笔记·学习
solicitous8 小时前
学习了解充电桩协议OCPP-架构与拓扑
学习·充电桩