本节课我们要学习的内容
-
在地图上随机生成红色敌人
-
玩家碰到敌人后,进入战斗模式
-
战斗胜利后敌人消失,获得分数
-
屏幕显示敌人数量
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 秒后自动反击
-
战斗结束:敌人死亡或玩家死亡,回到探索模式