本节课目标
-
按鼠标右键释放范围爆炸
-
爆炸有冷却时间(5秒)
-
屏幕显示冷却进度图标
-
爆炸范围内所有敌人受到大量伤害
-
爆炸有视觉特效
第一步:给 Player 类加技能
打开 Player.cs,添加以下内容:
1. 添加字段:
csharp
// 技能系统
private float skillCooldown = 5f;
private float skillTimer = 0f;
private bool skillReady = true;
2. 添加方法:
csharp
// 尝试释放技能,返回是否成功
public bool TryUseSkill(float deltaTime)
{
if (!skillReady)
{
skillTimer -= deltaTime;
if (skillTimer <= 0)
{
skillReady = true;
skillTimer = 0;
}
return false;
}
return true;
}
public void UseSkill()
{
skillReady = false;
skillTimer = skillCooldown;
}
// 技能冷却百分比(0 到 1,0=冷却好了)
public float GetSkillCooldownPercent()
{
if (skillReady) return 0;
return skillTimer / skillCooldown;
}
第二步:创建爆炸特效类
右键项目 → 添加 → 类 ,文件名 Explosion.cs:
cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
namespace MY_FIRST_GAME
{
public class Explosion
{
public Vector2 Position;
public float Radius;
public float MaxRadius;
public float Duration;
public float Timer;
public bool IsAlive => Timer < Duration;
public int Damage;
public Explosion(Vector2 position, float radius, float duration, int damage)
{
Position = position;
Radius = 0;
MaxRadius = radius;
Duration = duration;
Damage = damage;
Timer = 0;
}
public void Update(float deltaTime)
{
Timer += deltaTime;
float progress = Timer / Duration;
Radius = MaxRadius * progress; // 半径逐渐扩大
// 伤害在爆炸瞬间判定(progress < 0.1 时)
}
public void Draw(SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
{
float alpha = 1f - (Timer / Duration); // 逐渐透明
// 画扩散圆环
int segments = 32;
float angleStep = MathF.PI * 2 / segments;
for (int i = 0; i < segments; i++)
{
float angle1 = i * angleStep;
float angle2 = (i + 1) * angleStep;
float x1 = Position.X + MathF.Cos(angle1) * Radius;
float y1 = Position.Y + MathF.Sin(angle1) * Radius;
float x2 = Position.X + MathF.Cos(angle2) * Radius;
float y2 = Position.Y + MathF.Sin(angle2) * Radius;
// 简单的线段绘制(用 1x1 像素)
DrawLine(spriteBatch, graphicsDevice,
new Vector2(x1, y1), new Vector2(x2, y2),
Color.Orange * alpha, 3);
}
}
private void DrawLine(SpriteBatch spriteBatch, GraphicsDevice device,
Vector2 start, Vector2 end, Color color, int thickness)
{
Texture2D pixel = new Texture2D(device, 1, 1);
pixel.SetData(new[] { Color.White });
float distance = Vector2.Distance(start, end);
float angle = MathF.Atan2(end.Y - start.Y, end.X - start.X);
spriteBatch.Draw(pixel, start, null, color,
angle, Vector2.Zero,
new Vector2(distance, thickness),
SpriteEffects.None, 0);
}
public bool Intersects(Vector2 target, float targetRadius)
{
float dist = Vector2.Distance(Position, target);
return dist < Radius + targetRadius;
}
}
}
第三步:改造 Game1.cs
以下是需要改动的地方:
1. 添加字段:
csharp
private List<Explosion> explosions = default!;
2. 在 InitializeGame 里初始化:
csharp
explosions = new List<Explosion>();
3. 在 Update 的 Game 状态里添加技能触发和爆炸更新:
在子弹发射的代码后面加:
csharp
// ★ 右键释放技能
if (currentMouse.RightButton == ButtonState.Pressed &&
previousMouse.RightButton == ButtonState.Released)
{
if (player.TryUseSkill(deltaTime))
{
player.UseSkill();
Vector2 mouseWorld = camera.ScreenToWorld(
new Vector2(currentMouse.X, currentMouse.Y));
explosions.Add(new Explosion(mouseWorld, 120f, 0.5f, 60));
try { shootSound?.Play(); } catch { } // 可换成爆炸音效
}
}
在粒子更新后面加:
csharp
// 更新爆炸
foreach (Explosion exp in explosions)
exp.Update(deltaTime);
// ★ 爆炸伤害判定
for (int i = explosions.Count - 1; i >= 0; i--)
{
if (explosions[i].Timer < 0.1f) // 只在爆炸初期判定一次
{
for (int j = enemies.Count - 1; j >= 0; j--)
{
if (explosions[i].Intersects(enemies[j].Position, 20))
{
enemies[j].Hp -= explosions[i].Damage;
if (enemies[j].Hp <= 0)
{
enemies[j].IsAlive = false;
score += enemies[j].ScoreValue;
enemiesDefeatedThisGame++;
particleSystem.EmitHitParticles(enemies[j].Position);
// 加经验和掉落判定...
}
}
}
}
}
explosions.RemoveAll(e => !e.IsAlive);
4. 在 DrawGameWorld 里画爆炸:
csharp
foreach (Explosion exp in explosions)
exp.Draw(_spriteBatch, GraphicsDevice);
5. 替换 DrawGameUI 里添加技能冷却图标:
在 UI 最后加:
csharp
// ★ 技能冷却图标
DrawSkillCooldown(_spriteBatch, 700, 500, 40);
6. 添加冷却图标绘制方法:
csharp
private void DrawSkillCooldown(SpriteBatch spriteBatch, int x, int y, int size)
{
Texture2D pixel = new Texture2D(GraphicsDevice, 1, 1);
pixel.SetData(new[] { Color.White });
// 背景
spriteBatch.Draw(pixel, new Rectangle(x, y, size, size), Color.DarkSlateGray);
spriteBatch.Draw(pixel, new Rectangle(x + 2, y + 2, size - 4, size - 4), Color.Black);
// 冷却覆盖(从下往上)
float cooldownPercent = player.GetSkillCooldownPercent();
if (cooldownPercent > 0)
{
int coverHeight = (int)((size - 4) * cooldownPercent);
spriteBatch.Draw(pixel,
new Rectangle(x + 2, y + 2, size - 4, coverHeight),
Color.DarkBlue * 0.7f);
}
// 图标文字
_spriteBatch.DrawString(font, "⚡", new Vector2(x + 8, y + 6), Color.Gold);
_spriteBatch.DrawString(font, "右键", new Vector2(x - 15, y + size + 5), Color.LightGray);
}
好了,本节课学习就此结束,我叫魔法阵维护师,关注我,下期更精彩!