# 从零开始 - 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