本节课学习目标用 JSON 保存最高分到文件游戏结束时自动更新最高分标题画面显示最高分结束画面显示当前分数和最高分第一步创建存档管理类右键项目 →添加→类文件名SaveManager.csusing System.IO;using System.Text.Json;namespace MY_FIRST_GAME{public class SaveData{public int HighScore { get; set; }public int TotalCoinsCollected { get; set; }public int TotalEnemiesDefeated { get; set; }}public static class SaveManager{private static string filePath savedata.json;public static SaveData Load(){if (File.Exists(filePath)){string json File.ReadAllText(filePath);SaveData? data JsonSerializer.DeserializeSaveData(json);return data ?? new SaveData();}return new SaveData();}public static void Save(SaveData data){string json JsonSerializer.Serialize(data);File.WriteAllText(filePath, json);}}}第二步完整替换Game1.csusing Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Audio; using System; using System.Collections.Generic; using System.IO; using FontStashSharp; namespace MY_FIRST_GAME { public class Game1 : Game { private GraphicsDeviceManager _graphics; private SpriteBatch _spriteBatch; private SceneManager sceneManager default!; private Player player default!; private Texture2D playerSpriteSheet; private Texture2D coinTexture; private ListVector2 coins default!; private Random rng default!; private int score; private Texture2D enemyTexture; private ListVector2 enemies default!; private SpriteFontBase font default!; private SpriteFontBase titleFont default!; private SoundEffect coinSound default!; private SoundEffect hitSound default!; private ParticleSystem particleSystem default!; private float titleTimer; // ★ 存档数据 private SaveData saveData default!; private int coinsCollectedThisGame; private int enemiesDefeatedThisGame; private bool isNewHighScore; public Game1() { _graphics new GraphicsDeviceManager(this); Content.RootDirectory Content; IsMouseVisible true; } protected override void Initialize() { _graphics.PreferredBackBufferWidth 800; _graphics.PreferredBackBufferHeight 600; _graphics.ApplyChanges(); sceneManager new SceneManager(); rng new Random(); // ★ 加载存档 saveData SaveManager.Load(); InitializeGame(); base.Initialize(); } private void InitializeGame() { coins new ListVector2(); enemies new ListVector2(); score 0; coinsCollectedThisGame 0; enemiesDefeatedThisGame 0; isNewHighScore false; titleTimer 0f; SpawnCoins(5); SpawnEnemies(3); particleSystem new ParticleSystem(GraphicsDevice); } 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); titleFont fontSystem.GetFont(48); 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); } 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(); titleTimer deltaTime; sceneManager.Update(); switch (sceneManager.CurrentScene) { case SceneType.Title: if (Keyboard.GetState().IsKeyDown(Keys.Space) sceneManager.CurrentScene SceneType.Title) { player new Player(playerSpriteSheet, new Vector2(400, 300)); InitializeGame(); sceneManager.GoToGame(); } break; case SceneType.Game: player.Update(deltaTime); CheckCoinCollision(); CheckEnemyCollision(); if (coins.Count 0) SpawnCoins(5); if (enemies.Count 0) SpawnEnemies(3); particleSystem.Update(deltaTime); if (player.Hp 0) { // ★ 检测并保存最高分 if (score saveData.HighScore) { saveData.HighScore score; isNewHighScore true; } saveData.TotalCoinsCollected coinsCollectedThisGame; saveData.TotalEnemiesDefeated enemiesDefeatedThisGame; SaveManager.Save(saveData); sceneManager.GoToGameOver(); } break; case SceneType.GameOver: break; } if (keyboard.IsKeyDown(Keys.M)) 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)) { Vector2 coinPos coins[i]; coins.RemoveAt(i); score 10; coinsCollectedThisGame; particleSystem.EmitCoinParticles(coinPos); 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)) { Vector2 enemyPos enemies[i]; enemies.RemoveAt(i); score 50; enemiesDefeatedThisGame; player.Hp - 15; particleSystem.EmitHitParticles(enemyPos); try { hitSound?.Play(); } catch { } } } } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); _spriteBatch.Begin(); switch (sceneManager.CurrentScene) { case SceneType.Title: DrawTitle(); break; case SceneType.Game: DrawGame(); break; case SceneType.GameOver: DrawGameOver(); break; } _spriteBatch.End(); base.Draw(gameTime); } private void DrawTitle() { string title 史莱姆猎人; Vector2 titleSize titleFont.MeasureString(title); Vector2 titlePos new Vector2(400 - titleSize.X / 2, 120); float alpha (float)(Math.Sin(titleTimer * 3) 1) / 2 * 0.5f 0.5f; _spriteBatch.DrawString(titleFont, title, titlePos, Color.Gold * alpha); string subtitle 收集金币消灭史莱姆; Vector2 subSize font.MeasureString(subtitle); _spriteBatch.DrawString(font, subtitle, new Vector2(400 - subSize.X / 2, 200), Color.LightGray); // ★ 显示最高分 _spriteBatch.DrawString(font, $ 最高分{saveData.HighScore}, new Vector2(300, 260), Color.Gold); _spriteBatch.DrawString(font, $ 累计金币{saveData.TotalCoinsCollected}, new Vector2(300, 285), Color.Yellow); _spriteBatch.DrawString(font, $ 累计击杀{saveData.TotalEnemiesDefeated}, new Vector2(300, 310), Color.Red); string prompt 按 空格键 开始游戏; Vector2 promptSize font.MeasureString(prompt); _spriteBatch.DrawString(font, prompt, new Vector2(400 - promptSize.X / 2, 400), Color.White); } private void DrawGame() { 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); particleSystem.Draw(_spriteBatch); player.Draw(_spriteBatch); _spriteBatch.DrawString(font, $分数{score}, new Vector2(10, 10), Color.White); _spriteBatch.DrawString(font, $最高分{saveData.HighScore}, new Vector2(10, 35), Color.Gold); _spriteBatch.DrawString(font, $HP{player.Hp}/{player.MaxHp}, new Vector2(10, 60), Color.LimeGreen); _spriteBatch.DrawString(font, $本局金币{coinsCollectedThisGame}, new Vector2(10, 85), Color.Yellow); _spriteBatch.DrawString(font, WASD移动 | M静音 | ESC退出, new Vector2(10, 570), Color.LightGray); } private void DrawGameOver() { string title 游戏结束; Vector2 titleSize titleFont.MeasureString(title); _spriteBatch.DrawString(titleFont, title, new Vector2(400 - titleSize.X / 2, 100), Color.Red); string scoreText $本局分数{score}; Vector2 scoreSize font.MeasureString(scoreText); _spriteBatch.DrawString(font, scoreText, new Vector2(400 - scoreSize.X / 2, 190), Color.White); string highScoreText $ 最高分{saveData.HighScore}; Vector2 hsSize font.MeasureString(highScoreText); _spriteBatch.DrawString(font, highScoreText, new Vector2(400 - hsSize.X / 2, 220), Color.Gold); // ★ 新纪录提示 if (isNewHighScore) { string newRecord 新纪录; Vector2 nrSize font.MeasureString(newRecord); _spriteBatch.DrawString(font, newRecord, new Vector2(400 - nrSize.X / 2, 255), Color.Orange); } string coinsText $本局收集金币{coinsCollectedThisGame}; Vector2 cSize font.MeasureString(coinsText); _spriteBatch.DrawString(font, coinsText, new Vector2(400 - cSize.X / 2, 290), Color.Yellow); string enemiesText $本局消灭敌人{enemiesDefeatedThisGame}; Vector2 eSize font.MeasureString(enemiesText); _spriteBatch.DrawString(font, enemiesText, new Vector2(400 - eSize.X / 2, 315), Color.Red); string prompt 按 空格键 返回标题画面; Vector2 promptSize font.MeasureString(prompt); _spriteBatch.DrawString(font, prompt, new Vector2(400 - promptSize.X / 2, 400), Color.White); } } }本节课的学习到此结束我是魔法阵维护师关注我下期更精彩
从零开发游戏需要学习的c#模块,第二十三章(存档与高分系统)
本节课学习目标用 JSON 保存最高分到文件游戏结束时自动更新最高分标题画面显示最高分结束画面显示当前分数和最高分第一步创建存档管理类右键项目 →添加→类文件名SaveManager.csusing System.IO;using System.Text.Json;namespace MY_FIRST_GAME{public class SaveData{public int HighScore { get; set; }public int TotalCoinsCollected { get; set; }public int TotalEnemiesDefeated { get; set; }}public static class SaveManager{private static string filePath savedata.json;public static SaveData Load(){if (File.Exists(filePath)){string json File.ReadAllText(filePath);SaveData? data JsonSerializer.DeserializeSaveData(json);return data ?? new SaveData();}return new SaveData();}public static void Save(SaveData data){string json JsonSerializer.Serialize(data);File.WriteAllText(filePath, json);}}}第二步完整替换Game1.csusing Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Audio; using System; using System.Collections.Generic; using System.IO; using FontStashSharp; namespace MY_FIRST_GAME { public class Game1 : Game { private GraphicsDeviceManager _graphics; private SpriteBatch _spriteBatch; private SceneManager sceneManager default!; private Player player default!; private Texture2D playerSpriteSheet; private Texture2D coinTexture; private ListVector2 coins default!; private Random rng default!; private int score; private Texture2D enemyTexture; private ListVector2 enemies default!; private SpriteFontBase font default!; private SpriteFontBase titleFont default!; private SoundEffect coinSound default!; private SoundEffect hitSound default!; private ParticleSystem particleSystem default!; private float titleTimer; // ★ 存档数据 private SaveData saveData default!; private int coinsCollectedThisGame; private int enemiesDefeatedThisGame; private bool isNewHighScore; public Game1() { _graphics new GraphicsDeviceManager(this); Content.RootDirectory Content; IsMouseVisible true; } protected override void Initialize() { _graphics.PreferredBackBufferWidth 800; _graphics.PreferredBackBufferHeight 600; _graphics.ApplyChanges(); sceneManager new SceneManager(); rng new Random(); // ★ 加载存档 saveData SaveManager.Load(); InitializeGame(); base.Initialize(); } private void InitializeGame() { coins new ListVector2(); enemies new ListVector2(); score 0; coinsCollectedThisGame 0; enemiesDefeatedThisGame 0; isNewHighScore false; titleTimer 0f; SpawnCoins(5); SpawnEnemies(3); particleSystem new ParticleSystem(GraphicsDevice); } 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); titleFont fontSystem.GetFont(48); 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); } 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(); titleTimer deltaTime; sceneManager.Update(); switch (sceneManager.CurrentScene) { case SceneType.Title: if (Keyboard.GetState().IsKeyDown(Keys.Space) sceneManager.CurrentScene SceneType.Title) { player new Player(playerSpriteSheet, new Vector2(400, 300)); InitializeGame(); sceneManager.GoToGame(); } break; case SceneType.Game: player.Update(deltaTime); CheckCoinCollision(); CheckEnemyCollision(); if (coins.Count 0) SpawnCoins(5); if (enemies.Count 0) SpawnEnemies(3); particleSystem.Update(deltaTime); if (player.Hp 0) { // ★ 检测并保存最高分 if (score saveData.HighScore) { saveData.HighScore score; isNewHighScore true; } saveData.TotalCoinsCollected coinsCollectedThisGame; saveData.TotalEnemiesDefeated enemiesDefeatedThisGame; SaveManager.Save(saveData); sceneManager.GoToGameOver(); } break; case SceneType.GameOver: break; } if (keyboard.IsKeyDown(Keys.M)) 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)) { Vector2 coinPos coins[i]; coins.RemoveAt(i); score 10; coinsCollectedThisGame; particleSystem.EmitCoinParticles(coinPos); 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)) { Vector2 enemyPos enemies[i]; enemies.RemoveAt(i); score 50; enemiesDefeatedThisGame; player.Hp - 15; particleSystem.EmitHitParticles(enemyPos); try { hitSound?.Play(); } catch { } } } } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); _spriteBatch.Begin(); switch (sceneManager.CurrentScene) { case SceneType.Title: DrawTitle(); break; case SceneType.Game: DrawGame(); break; case SceneType.GameOver: DrawGameOver(); break; } _spriteBatch.End(); base.Draw(gameTime); } private void DrawTitle() { string title 史莱姆猎人; Vector2 titleSize titleFont.MeasureString(title); Vector2 titlePos new Vector2(400 - titleSize.X / 2, 120); float alpha (float)(Math.Sin(titleTimer * 3) 1) / 2 * 0.5f 0.5f; _spriteBatch.DrawString(titleFont, title, titlePos, Color.Gold * alpha); string subtitle 收集金币消灭史莱姆; Vector2 subSize font.MeasureString(subtitle); _spriteBatch.DrawString(font, subtitle, new Vector2(400 - subSize.X / 2, 200), Color.LightGray); // ★ 显示最高分 _spriteBatch.DrawString(font, $ 最高分{saveData.HighScore}, new Vector2(300, 260), Color.Gold); _spriteBatch.DrawString(font, $ 累计金币{saveData.TotalCoinsCollected}, new Vector2(300, 285), Color.Yellow); _spriteBatch.DrawString(font, $ 累计击杀{saveData.TotalEnemiesDefeated}, new Vector2(300, 310), Color.Red); string prompt 按 空格键 开始游戏; Vector2 promptSize font.MeasureString(prompt); _spriteBatch.DrawString(font, prompt, new Vector2(400 - promptSize.X / 2, 400), Color.White); } private void DrawGame() { 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); particleSystem.Draw(_spriteBatch); player.Draw(_spriteBatch); _spriteBatch.DrawString(font, $分数{score}, new Vector2(10, 10), Color.White); _spriteBatch.DrawString(font, $最高分{saveData.HighScore}, new Vector2(10, 35), Color.Gold); _spriteBatch.DrawString(font, $HP{player.Hp}/{player.MaxHp}, new Vector2(10, 60), Color.LimeGreen); _spriteBatch.DrawString(font, $本局金币{coinsCollectedThisGame}, new Vector2(10, 85), Color.Yellow); _spriteBatch.DrawString(font, WASD移动 | M静音 | ESC退出, new Vector2(10, 570), Color.LightGray); } private void DrawGameOver() { string title 游戏结束; Vector2 titleSize titleFont.MeasureString(title); _spriteBatch.DrawString(titleFont, title, new Vector2(400 - titleSize.X / 2, 100), Color.Red); string scoreText $本局分数{score}; Vector2 scoreSize font.MeasureString(scoreText); _spriteBatch.DrawString(font, scoreText, new Vector2(400 - scoreSize.X / 2, 190), Color.White); string highScoreText $ 最高分{saveData.HighScore}; Vector2 hsSize font.MeasureString(highScoreText); _spriteBatch.DrawString(font, highScoreText, new Vector2(400 - hsSize.X / 2, 220), Color.Gold); // ★ 新纪录提示 if (isNewHighScore) { string newRecord 新纪录; Vector2 nrSize font.MeasureString(newRecord); _spriteBatch.DrawString(font, newRecord, new Vector2(400 - nrSize.X / 2, 255), Color.Orange); } string coinsText $本局收集金币{coinsCollectedThisGame}; Vector2 cSize font.MeasureString(coinsText); _spriteBatch.DrawString(font, coinsText, new Vector2(400 - cSize.X / 2, 290), Color.Yellow); string enemiesText $本局消灭敌人{enemiesDefeatedThisGame}; Vector2 eSize font.MeasureString(enemiesText); _spriteBatch.DrawString(font, enemiesText, new Vector2(400 - eSize.X / 2, 315), Color.Red); string prompt 按 空格键 返回标题画面; Vector2 promptSize font.MeasureString(prompt); _spriteBatch.DrawString(font, prompt, new Vector2(400 - promptSize.X / 2, 400), Color.White); } } }本节课的学习到此结束我是魔法阵维护师关注我下期更精彩