本节课学习目标
-
加载并播放背景音乐(循环)
-
收集金币时播放音效
-
碰到敌人时播放音效
-
用 MonoGame 内置音频系统实现
第一步:准备音频文件
去这些网站下载免费音效:
需要三个文件:
| 文件 | 用途 |
|---|---|
coin.wav |
吃金币音效 |
hit.wav |
碰敌人音效 |
bgm.wav 或 bgm.mp3 |
背景音乐 |
MonoGame 支持的格式 :.wav、.mp3、.ogg
下载后放到 Content 文件夹,属性设为 "如果较新则复制"。
第二步:完整代码
把 Game1.cs 完整替换为:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;
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 Player player = default!;
private Texture2D playerSpriteSheet;
private Texture2D coinTexture;
private List<Vector2> coins;
private Random rng;
private int score;
private Texture2D enemyTexture;
private List<Vector2> enemies;
private SpriteFontBase font;
private GameState state = GameState.Exploring;
// ★ 音频
private SoundEffect coinSound = default!;
private SoundEffect hitSound = default!;
private Song bgm = default!;
public Game1()
{
_graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IsMouseVisible = true;
// ★ 背景音乐必须通过 MGCB Editor 加载
// 如果 MGCB 不能用,这里会跳过音乐加载
}
protected override void Initialize()
{
_graphics.PreferredBackBufferWidth = 800;
_graphics.PreferredBackBufferHeight = 600;
_graphics.ApplyChanges();
rng = new Random();
coins = new List<Vector2>();
enemies = new List<Vector2>();
score = 0;
SpawnCoins(5);
SpawnEnemies(3);
base.Initialize();
}
protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
// 玩家图集
using var stream = File.OpenRead("Content/player_spritesheet.png");
playerSpriteSheet = Texture2D.FromStream(GraphicsDevice, stream);
player = new Player(playerSpriteSheet, new Vector2(400, 300));
// 金币
coinTexture = new Texture2D(GraphicsDevice, 24, 24);
Color[] coinData = new Color[24 * 24];
for (int i = 0; i < coinData.Length; i++) coinData[i] = Color.Gold;
coinTexture.SetData(coinData);
// 敌人
enemyTexture = new Texture2D(GraphicsDevice, 40, 40);
Color[] enemyData = new Color[40 * 40];
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);
// ★ 加载音效(SoundEffect 可以用 FileStream 直接读)
try
{
using var coinStream = File.OpenRead("Content/coin.wav");
coinSound = SoundEffect.FromStream(coinStream);
using var hitStream = File.OpenRead("Content/hit.wav");
hitSound = SoundEffect.FromStream(hitStream);
// 背景音乐(MediaPlayer 需要 MGCB 编译,直接读文件不支持)
// 暂时用音效替代,把 bgm 循环播放
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("音效加载失败:" + ex.Message);
}
}
// ★ 开始播放背景音乐(在 Initialize 之后调用)
protected override void BeginRun()
{
base.BeginRun();
// 尝试播放背景音乐(音效作为临时方案)
try
{
// 如果你的 bgm 是 mp3/wav,可以用 SoundEffect 播放
using var bgmStream = File.OpenRead("Content/bgm.wav");
var bgmSound = SoundEffect.FromStream(bgmStream);
SoundEffectInstance bgmInstance = bgmSound.CreateInstance();
bgmInstance.IsLooped = true;
bgmInstance.Volume = 0.3f; // 音量 30%
bgmInstance.Play();
}
catch
{
// 没有音乐文件就跳过
}
}
private void SpawnCoins(int count)
{
for (int i = 0; i < count; i++)
coins.Add(new Vector2(rng.Next(50, 750), rng.Next(50, 550)));
}
private void SpawnEnemies(int count)
{
for (int i = 0; i < count; i++)
enemies.Add(new Vector2(rng.Next(80, 720), rng.Next(80, 520)));
}
protected override void Update(GameTime gameTime)
{
float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
KeyboardState keyboard = Keyboard.GetState();
if (state == GameState.Exploring)
{
player.Update(deltaTime);
CheckCoinCollision();
CheckEnemyCollision();
if (coins.Count == 0) SpawnCoins(5);
if (enemies.Count == 0) SpawnEnemies(3);
}
if (keyboard.IsKeyDown(Keys.M))
{
// ★ 按 M 静音/取消静音
MediaPlayer.IsMuted = !MediaPlayer.IsMuted;
SoundEffect.MasterVolume = SoundEffect.MasterVolume > 0 ? 0f : 1f;
}
if (keyboard.IsKeyDown(Keys.Escape)) Exit();
base.Update(gameTime);
}
private void CheckCoinCollision()
{
Rectangle playerRect = player.GetBounds();
for (int i = coins.Count - 1; i >= 0; i--)
{
Rectangle coinRect = new Rectangle((int)coins[i].X, (int)coins[i].Y, 24, 24);
if (playerRect.Intersects(coinRect))
{
coins.RemoveAt(i);
score += 10;
// ★ 播放吃金币音效
try { coinSound?.Play(); }
catch { }
}
}
}
private void CheckEnemyCollision()
{
Rectangle playerRect = player.GetBounds();
for (int i = enemies.Count - 1; i >= 0; i--)
{
Rectangle enemyRect = new Rectangle(
(int)enemies[i].X - 20, (int)enemies[i].Y - 20, 40, 40);
if (playerRect.Intersects(enemyRect))
{
enemies.RemoveAt(i);
score += 50;
// ★ 播放碰敌人音效
try { hitSound?.Play(); }
catch { }
}
}
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
_spriteBatch.Begin();
foreach (Vector2 coinPos in coins)
_spriteBatch.Draw(coinTexture, coinPos, Color.White);
foreach (Vector2 enemyPos in enemies)
_spriteBatch.Draw(enemyTexture, enemyPos, null, Color.White,
0f, new Vector2(20, 20), 1f, SpriteEffects.None, 0f);
player.Draw(_spriteBatch);
_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:{player.Hp}/{player.MaxHp}", new Vector2(10, 85), Color.LimeGreen);
_spriteBatch.DrawString(font, "WASD移动 | M静音 | ESC退出", new Vector2(10, 570), Color.LightGray);
_spriteBatch.End();
base.Draw(gameTime);
}
}
}
本节课新内容
cs
using var stream = File.OpenRead("Content/coin.wav");
coinSound = SoundEffect.FromStream(stream);
coinSound.Play(); // 播放一次
cs
SoundEffectInstance bgmInstance = bgmSound.CreateInstance();
bgmInstance.IsLooped = true; // 循环播放
bgmInstance.Volume = 0.3f; // 音量
bgmInstance.Play();
cs
SoundEffect.MasterVolume = 0f; // 所有音效静音
SoundEffect.MasterVolume = 1f; // 恢复
本节课到此结束,我叫魔法阵维护师,关注我,下期更精彩!