# 从零开始 - GFramework 游戏开发教程 > 这是一个完整的从零开始的教程,将带领你创建一个使用 GFramework 的简单游戏项目。 ## 📋 目录 - [环境准备](#环境准备) - [项目创建](#项目创建) - [架构设计](#架构设计) - [功能实现](#功能实现) - [测试验证](#测试验证) - [项目打包](#项目打包) - [进阶功能](#进阶功能) ## 环境准备 ### 系统要求 - **操作系统**: Windows 10+, macOS 10.15+, 或 Linux - **.NET SDK**: 6.0 或更高版本 - **Godot 引擎**: 4.5.1 或更高版本 - **IDE**: Visual Studio 2022+, JetBrains Rider, 或 VS Code ### 安装 .NET SDK 1. 访问 [.NET 官网](https://dotnet.microsoft.com/download) 2. 下载并安装 .NET 6.0 SDK 3. 验证安装: ```bash dotnet --version # 应该显示 6.0.x 或更高版本 ``` ### 安装 Godot 1. 访问 [Godot 官网](https://godotengine.org/download) 2. 下载 Godot 4.5.1 3. 解压到合适的位置并启动 4. 在编辑器设置中确认 .NET 支持 ### 验证环境 创建一个测试项目验证环境: ```bash # 创建测试项目 dotnet new console -n TestProject cd TestProject # 如果使用 Godot,添加 Godot 引用 dotnet add package GeWuYou.GFramework.Core dotnet add package GeWuYou.GFramework.Godot # 编译测试 dotnet build ``` ## 项目创建 ### 1. 创建新的 Godot 项目 1. 打开 Godot 编辑器 2. 点击 "新建项目" 3. 创建项目文件夹,命名为 "MyGFrameworkGame" 4. 选择 C# 作为脚本语言 ### 2. 配置项目结构 在项目根目录创建以下文件夹结构: ``` MyGFrameworkGame/ ├── src/ │ ├── Game/ # 游戏逻辑 │ │ ├── Models/ # 数据模型 │ │ ├── Systems/ # 业务系统 │ │ ├── Controllers/ # 控制器 │ │ └── Utilities/ # 工具类 │ └── Game.Core/ # 核心游戏组件 ├── assets/ # 游戏资源 │ ├── scenes/ │ ├── textures/ │ ├── audio/ │ └── ui/ └── project.godot # Godot 项目文件 ``` ### 3. 配置项目文件 创建 `src/Game/Game.csproj`: ```xml net6.0 MyGFrameworkGame enable ``` 创建 `src/Game.Core/Game.Core.csproj`: ```xml net6.0 MyGFrameworkGame.Core enable ``` ### 4. 配置 Godot 项目 在 `project.godot` 中添加 C# 配置: ```ini ; Engine configuration file. ; It's best edited using the editor UI and not directly, ; since the parameters that go here are not all obvious. ; ; Format: ; [section] ; section goes between [] ; param=value ; assign values to parameters [application] config/name="My GFramework Game" config/description="A game built with GFramework" run/main_scene="res://src/Game/MainScene.tscn" config/features=PackedStringArray("4.5", "Forward Plus") [dotnet] project/assembly_name="MyGFrameworkGame" project/solution_directory="src/" ``` ## 架构设计 ### 1. 定义游戏架构 创建 `src/Game.Core/Architecture/GameArchitecture.cs`: ```csharp using GFramework.Core.architecture; using MyGFrameworkGame.Core.Models; using MyGFrameworkGame.Core.Systems; using MyGFrameworkGame.Core.Utilities; namespace MyGFrameworkGame.Core.Architecture { public class GameArchitecture : AbstractArchitecture { protected override void Init() { // 注册游戏模型 RegisterModel(new PlayerModel()); RegisterModel(new GameModel()); RegisterModel(new ScoreModel()); // 注册游戏系统 RegisterSystem(new PlayerControllerSystem()); RegisterSystem(new EnemySpawnerSystem()); RegisterSystem(new CollisionSystem()); RegisterSystem(new ScoreSystem()); // 注册工具类 RegisterUtility(new StorageUtility()); RegisterUtility(new AudioUtility()); RegisterUtility(new AssetLoadUtility()); } protected override void InstallModules() { // 如果需要 Godot 特定模块 // InstallGodotModule(new AudioModule()); } } } ``` ### 2. 创建核心模型 创建 `src/Game.Core/Models/PlayerModel.cs`: ```csharp using GFramework.Core.model; namespace MyGFrameworkGame.Core.Models { public class PlayerModel : AbstractModel { public BindableProperty Health { get; } = new(100); public BindableProperty MaxHealth { get; } = new(100); public BindableProperty Speed { get; } = new(300.0f); public BindableProperty Score { get; } = new(0); public BindableProperty IsAlive { get; } = new(true); public BindableProperty CanShoot { get; } = new(true); protected override void OnInit() { // 监听生命值变化 Health.Register(health => { if (health <= 0) { IsAlive.Value = false; SendEvent(new PlayerDeathEvent()); } }); // 监听分数变化 Score.Register(score => { if (score % 100 == 0) { SendEvent(new MilestoneEvent { Score = score }); } }); } public void TakeDamage(int damage) { if (IsAlive.Value) { var newHealth = Math.Max(0, Health.Value - damage); Health.Value = newHealth; } } public void Heal(int amount) { var newHealth = Math.Min(MaxHealth.Value, Health.Value + amount); Health.Value = newHealth; } public bool CanShoot() { return IsAlive.Value && CanShoot.Value; } } // 游戏事件 public struct PlayerDeathEvent { } public struct MilestoneEvent { public int Score; } } ``` 创建 `src/Game.Core/Models/GameModel.cs`: ```csharp using GFramework.Core.model; namespace MyGFrameworkGame.Core.Models { public class GameModel : AbstractModel { public BindableProperty State { get; } = new(GameState.Menu); public BindableProperty CurrentLevel { get; } = new(1); public BindableProperty EnemyCount { get; } = new(0); public BindableProperty GameTime { get; } = new(0.0f); public BindableProperty IsPaused { get; } = new(false); protected override void OnInit() { State.Register(state => { switch (state) { case GameState.Playing: SendEvent(new GameStartEvent()); break; case GameState.Paused: SendEvent(new GamePauseEvent()); break; case GameState.GameOver: SendEvent(new GameOverEvent()); break; case GameState.Menu: SendEvent(new GameMenuEvent()); break; } }); } public void StartGame() { State.Value = GameState.Playing; EnemyCount.Value = 0; GameTime.Value = 0.0f; } public void PauseGame() { if (State.Value == GameState.Playing) { State.Value = GameState.Paused; } } public void ResumeGame() { if (State.Value == GameState.Paused) { State.Value = GameState.Playing; } } public void GameOver() { State.Value = GameState.GameOver; } public void ReturnToMenu() { State.Value = GameState.Menu; } } public enum GameState { Menu, Playing, Paused, GameOver } // 游戏事件 public struct GameStartEvent { } public struct GamePauseEvent { } public struct GameResumeEvent { } public struct GameOverEvent { } public struct GameMenuEvent { } } ``` ### 3. 创建核心系统 创建 `src/Game.Core/Systems/PlayerControllerSystem.cs`: ```csharp using Godot; using GFramework.Core.system; using GFramework.Core.events; using MyGFrameworkGame.Core.Models; namespace MyGFrameworkGame.Core.Systems { public class PlayerControllerSystem : AbstractSystem { private PlayerModel _playerModel; private GameModel _gameModel; protected override void OnInit() { _playerModel = GetModel(); _gameModel = GetModel(); // 监听输入事件 RegisterEvent(OnPlayerInput); RegisterEvent(OnGameStart); RegisterEvent(OnGameOver); } private void OnPlayerInput(PlayerInputEvent inputEvent) { if (_gameModel.State.Value != GameState.Playing) return; switch (inputEvent.Action) { case "move": HandleMovement(inputEvent.Direction); break; case "shoot": HandleShoot(); break; case "pause": _gameModel.PauseGame(); break; } } private void HandleMovement(Vector2 direction) { if (!_playerModel.IsAlive.Value) return; // 发送移动事件,由 Godot 控制器处理 SendEvent(new PlayerMoveEvent { Direction = direction.Normalized() * _playerModel.Speed.Value }); } private void HandleShoot() { if (!_playerModel.CanShoot()) return; // 发送射击事件 SendEvent(new PlayerShootEvent()); _playerModel.CanShoot.Value = false; // 重置射击冷却 this.StartCoroutine(ShootCooldown()); } private System.Collections.IEnumerator ShootCooldown() { yield return new WaitForSeconds(0.3f); _playerModel.CanShoot.Value = true; } private void OnGameStart(GameStartEvent e) { // 重置玩家状态 _playerModel.Health.Value = _playerModel.MaxHealth.Value; _playerModel.Score.Value = 0; _playerModel.IsAlive.Value = true; _playerModel.CanShoot.Value = true; } private void OnGameOver(GameOverEvent e) { // 游戏结束时的处理 SendEvent(new SaveScoreEvent { Score = _playerModel.Score.Value }); } } // 输入事件 public struct PlayerInputEvent { public string Action { get; set; } public Vector2 Direction { get; set; } } // 移动事件 public struct PlayerMoveEvent { public Vector2 Direction { get; set; } } // 射击事件 public struct PlayerShootEvent { } // 保存分数事件 public struct SaveScoreEvent { public int Score; } // 协程等待类(简化版) public class WaitForSeconds { public float Seconds { get; } public WaitForSeconds(float seconds) => Seconds = seconds; } } ``` ## 功能实现 ### 1. 创建主场景 创建 `src/Game/MainScene.cs`: ```csharp using Godot; using GFramework.Godot.extensions; using GFramework.Godot.architecture; using MyGFrameworkGame.Core.Architecture; using MyGFrameworkGame.Core.Models; using MyGFrameworkGame.Core.Systems; namespace MyGFrameworkGame { [ContextAware] [Log] public partial class MainScene : Node2D { private GameArchitecture _architecture; private Player _player; private UI _ui; private EnemySpawner _enemySpawner; public override void _Ready() { Logger.Info("Main scene ready"); // 初始化架构 InitializeArchitecture(); // 创建游戏对象 CreateGameObjects(); // 注册事件监听 RegisterEventListeners(); // 开始游戏 StartGame(); } private void InitializeArchitecture() { Logger.Info("Initializing game architecture"); _architecture = new GameArchitecture(); _architecture.Initialize(); // 设置上下文 SetContext(_architecture.Context); } private void CreateGameObjects() { Logger.Info("Creating game objects"); // 创建玩家 var playerScene = GD.Load("res://assets/scenes/Player.tscn"); _player = playerScene.Instantiate(); AddChild(_player); // 创建 UI var uiScene = GD.Load("res://assets/scenes/UI.tscn"); _ui = uiScene.Instantiate(); AddChild(_ui); // 创建敌人生成器 _enemySpawner = new EnemySpawner(); AddChild(_enemySpawner); } private void RegisterEventListeners() { Logger.Info("Registering event listeners"); // 游戏状态事件 this.RegisterEvent(OnGameStart) .UnRegisterWhenNodeExitTree(this); this.RegisterEvent(OnGameOver) .UnRegisterWhenNodeExitTree(this); this.RegisterEvent(OnGamePause) .UnRegisterWhenNodeExitTree(this); // 玩家事件 this.RegisterEvent(OnPlayerDeath) .UnRegisterWhenNodeExitTree(this); // 分数事件 this.RegisterEvent(OnSaveScore) .UnRegisterWhenNodeExitTree(this); } private void StartGame() { Logger.Info("Starting game"); var gameModel = Context.GetModel(); gameModel.StartGame(); } private void OnGameStart(GameStartEvent e) { Logger.Info("Game started"); _ui.ShowGameplayUI(); } private void OnGameOver(GameOverEvent e) { Logger.Info("Game over"); _ui.ShowGameOverScreen(); } private void OnGamePause(GamePauseEvent e) { Logger.Info("Game paused"); _ui.ShowPauseMenu(); GetTree().Paused = true; } private void OnPlayerDeath(PlayerDeathEvent e) { Logger.Info("Player died"); var gameModel = Context.GetModel(); gameModel.GameOver(); } private void OnSaveScore(SaveScoreEvent e) { Logger.Info($"Saving score: {e.Score}"); _ui.UpdateFinalScore(e.Score); } public override void _Input(InputEvent @event) { // 处理退出游戏 if (@event.IsActionPressed("ui_cancel")) { var gameModel = Context.GetModel(); if (gameModel.State.Value == GameState.Playing) { gameModel.PauseGame(); } else if (gameModel.State.Value == GameState.Paused) { gameModel.ResumeGame(); GetTree().Paused = false; } } } protected override void Dispose(bool disposing) { if (disposing) { _architecture?.Destroy(); } base.Dispose(disposing); } } } ``` ### 2. 创建玩家控制器 创建 `src/Game/Player.cs`: ```csharp using Godot; using GFramework.Godot.extensions; using GFramework.Core.events; using MyGFrameworkGame.Core.Models; using MyGFrameworkGame.Core.Systems; namespace MyGFrameworkGame { [ContextAware] [Log] public partial class Player : CharacterBody2D { [Export] public float Speed { get; set; } = 300.0f; private PlayerModel _playerModel; private GameModel _gameModel; private AnimatedSprite2D _animatedSprite; private CollisionShape2D _collisionShape; private Timer _invincibilityTimer; public override void _Ready() { Logger.Info("Player ready"); // 获取组件引用 _animatedSprite = GetNode("AnimatedSprite2D"); _collisionShape = GetNode("CollisionShape2D"); _invincibilityTimer = GetNode("InvincibilityTimer"); // 设置上下文 _playerModel = Context.GetModel(); _gameModel = Context.GetModel(); // 注册事件监听 RegisterEventListeners(); // 设置动画 _animatedSprite.Play("idle"); } private void RegisterEventListeners() { // 移动事件 this.RegisterEvent(OnPlayerMove) .UnRegisterWhenNodeExitTree(this); // 射击事件 this.RegisterEvent(OnPlayerShoot) .UnRegisterWhenNodeExitTree(this); // 玩家状态变化 _playerModel.Health.Register(OnHealthChanged) .UnRegisterWhenNodeExitTree(this); _playerModel.IsAlive.Register(OnAliveChanged) .UnRegisterWhenNodeExitTree(this); } public override void _Process(double delta) { // 处理输入并发送事件 HandleInput(); // 更新位置 var collision = MoveAndCollide(Velocity * (float)delta); if (collision != null) { HandleCollision(collision); } } private void HandleInput() { var inputVector = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down"); if (inputVector != Vector2.Zero) { // 发送移动事件 SendEvent(new PlayerInputEvent { Action = "move", Direction = inputVector }); // 更新动画 UpdateAnimation(inputVector); } // 射击输入 if (Input.IsActionJustPressed("shoot")) { SendEvent(new PlayerInputEvent { Action = "shoot" }); } } private void OnPlayerMove(PlayerMoveEvent e) { Velocity = e.Direction * Speed; } private void OnPlayerShoot(PlayerShootEvent e) { Shoot(); } private void OnHealthChanged(int newHealth) { Logger.Debug($"Player health changed: {newHealth}"); // 受伤效果 if (newHealth < _playerModel.MaxHealth.Value) { FlashRed(); StartInvincibility(); } } private void OnAliveChanged(bool isAlive) { if (!isAlive) { Die(); } } private void Shoot() { Logger.Debug("Player shooting"); // 创建子弹 var bulletScene = GD.Load("res://assets/scenes/Bullet.tscn"); var bullet = bulletScene.Instantiate(); // 设置子弹位置和方向 bullet.Position = Position; bullet.Direction = Vector2.Up; // 向上射击 // 添加到场景 GetTree().Root.AddChild(bullet); // 播放射击音效 PlayShootSound(); } private void UpdateAnimation(Vector2 inputVector) { if (inputVector.Y < 0) { _animatedSprite.Play("up"); } else if (inputVector.Y > 0) { _animatedSprite.Play("down"); } else if (inputVector.X != 0) { _animatedSprite.Play("side"); _animatedSprite.FlipH = inputVector.X < 0; } else { _animatedSprite.Play("idle"); } } private void FlashRed() { _animatedSprite.Modulate = Colors.Red; this.CreateSignalBuilder(_invincibilityTimer.SignalName.Timeout) .Connect(() => _animatedSprite.Modulate = Colors.White) .UnRegisterWhenNodeExitTree(this); } private void StartInvincibility() { SetCollisionLayerValue(1, false); // 关闭碰撞 _invincibilityTimer.Start(); } private void HandleCollision(KinematicCollision2D collision) { var collider = collision.GetCollider(); if (collider is Enemy enemy && _playerModel.IsAlive.Value) { // 受到伤害 _playerModel.TakeDamage(enemy.Damage); } } private void Die() { Logger.Info("Player died"); // 播放死亡动画 _animatedSprite.Play("death"); // 禁用碰撞 _collisionShape.Disabled = true; // 发送玩家死亡事件 SendEvent(new PlayerDeathEvent()); // 延迟移除 this.CreateSignalBuilder(_animatedSprite.SignalName.AnimationFinished) .WithFlags(ConnectFlags.OneShot) .Connect(() => QueueFreeX()) .UnRegisterWhenNodeExitTree(this); } private void PlayShootSound() { var audioPlayer = new AudioStreamPlayer(); var sound = GD.Load("res://assets/audio/shoot.wav"); audioPlayer.Stream = sound; AddChild(audioPlayer); audioPlayer.Play(); // 音效播放完成后移除 this.CreateSignalBuilder(audioPlayer.SignalName.Finished) .WithFlags(ConnectFlags.OneShot) .Connect(() => audioPlayer.QueueFreeX()) .UnRegisterWhenNodeExitTree(this); } } } ``` ### 3. 创建 UI 系统 创建 `src/Game/UI.cs`: ```csharp using Godot; using GFramework.Godot.extensions; using GFramework.Core.events; using MyGFrameworkGame.Core.Models; namespace MyGFrameworkGame { [ContextAware] [Log] public partial class UI : CanvasLayer { private Control _gameplayUI; private Control _pauseMenu; private Control _gameOverScreen; private Label _scoreLabel; private Label _healthLabel; private Label _finalScoreLabel; private Button _resumeButton; private Button _quitButton; private Button _restartButton; private PlayerModel _playerModel; private GameModel _gameModel; public override void _Ready() { Logger.Info("UI ready"); // 获取 UI 组件引用 InitializeUIComponents(); // 设置上下文 _playerModel = Context.GetModel(); _gameModel = Context.GetModel(); // 注册事件监听 RegisterEventListeners(); // 注册按钮事件 RegisterButtonEvents(); // 显示主菜单 ShowMainMenu(); } private void InitializeUIComponents() { // 游戏 UI _gameplayUI = GetNode("GameplayUI"); _scoreLabel = GetNode