---
title: Godot 完整项目搭建
description: 从零开始使用 GFramework 构建一个完整的 Godot 游戏项目
---
# Godot 完整项目搭建
## 学习目标
完成本教程后,你将能够:
- 在 Godot 项目中集成 GFramework
- 创建完整的游戏架构
- 实现场景管理和 UI 系统
- 使用协程和事件系统
- 实现游戏存档功能
- 构建一个可运行的完整游戏
## 前置条件
- 已安装 Godot 4.x
- 已安装 .NET SDK 8.0+
- 了解 C# 和 Godot 基础
- 阅读过前面的教程:
- [使用协程系统](/zh-CN/tutorials/coroutine-tutorial)
- [实现状态机](/zh-CN/tutorials/state-machine-tutorial)
- [实现存档系统](/zh-CN/tutorials/save-system)
## 项目概述
我们将创建一个简单的 2D 射击游戏,包含以下功能:
- 主菜单和游戏场景
- 玩家控制和射击
- 敌人生成和 AI
- 分数和生命值系统
- 游戏存档和加载
- 暂停菜单
## 步骤 1:创建 Godot 项目并配置
首先创建 Godot 项目并添加 GFramework 依赖。
### 1.1 创建项目
1. 打开 Godot,创建新项目 "MyShooterGame"
2. 选择 C# 作为脚本语言
3. 创建项目后,在项目根目录创建 `.csproj` 文件
### 1.2 添加 NuGet 包
编辑 `MyShooterGame.csproj`:
```xml
net8.0
true
12
enable
```
### 1.3 创建项目结构
```
MyShooterGame/
├── Scripts/
│ ├── Architecture/
│ │ └── GameArchitecture.cs
│ ├── Models/
│ │ ├── PlayerModel.cs
│ │ └── GameModel.cs
│ ├── Systems/
│ │ ├── GameplaySystem.cs
│ │ └── SpawnSystem.cs
│ ├── Controllers/
│ │ └── PlayerController.cs
│ └── Data/
│ └── GameSaveData.cs
├── Scenes/
│ ├── Main.tscn
│ ├── Menu.tscn
│ ├── Game.tscn
│ ├── Player.tscn
│ └── Enemy.tscn
└── UI/
├── MainMenu.tscn
├── HUD.tscn
└── PauseMenu.tscn
```
**代码说明**:
- 使用 Godot.NET.Sdk 4.3.0
- 添加 GFramework 的三个核心包
- 按功能组织代码结构
## 步骤 2:创建游戏架构
实现游戏的核心架构和数据模型。
### 2.1 定义数据模型
```csharp
// Scripts/Models/PlayerModel.cs
using GFramework.Core.model;
using GFramework.Core.Abstractions.property;
namespace MyShooterGame.Models
{
public class PlayerModel : AbstractModel
{
public BindableProperty Health { get; } = new(100);
public BindableProperty MaxHealth { get; } = new(100);
public BindableProperty Score { get; } = new(0);
public BindableProperty Lives { get; } = new(3);
public BindableProperty IsAlive { get; } = new(true);
protected override void OnInit()
{
// 监听生命值变化
Health.RegisterOnValueChanged(health =>
{
if (health <= 0)
{
IsAlive.Value = false;
}
});
}
public void Reset()
{
Health.Value = MaxHealth.Value;
Score.Value = 0;
Lives.Value = 3;
IsAlive.Value = true;
}
public void TakeDamage(int damage)
{
Health.Value = Math.Max(0, Health.Value - damage);
}
public void AddScore(int points)
{
Score.Value += points;
}
public void LoseLife()
{
Lives.Value = Math.Max(0, Lives.Value - 1);
if (Lives.Value > 0)
{
Health.Value = MaxHealth.Value;
IsAlive.Value = true;
}
}
}
}
```
```csharp
// Scripts/Models/GameModel.cs
using GFramework.Core.model;
using GFramework.Core.Abstractions.property;
namespace MyShooterGame.Models
{
public class GameModel : AbstractModel
{
public BindableProperty IsPlaying { get; } = new(false);
public BindableProperty IsPaused { get; } = new(false);
public BindableProperty CurrentWave { get; } = new(1);
public BindableProperty EnemiesAlive { get; } = new(0);
public BindableProperty GameTime { get; } = new(0f);
protected override void OnInit()
{
// 初始化
}
public void StartGame()
{
IsPlaying.Value = true;
IsPaused.Value = false;
CurrentWave.Value = 1;
EnemiesAlive.Value = 0;
GameTime.Value = 0f;
}
public void PauseGame()
{
IsPaused.Value = true;
}
public void ResumeGame()
{
IsPaused.Value = false;
}
public void EndGame()
{
IsPlaying.Value = false;
IsPaused.Value = false;
}
}
}
```
### 2.2 定义存档数据
```csharp
// Scripts/Data/GameSaveData.cs
using GFramework.Game.Abstractions.data;
using System;
namespace MyShooterGame.Data
{
public class GameSaveData : IVersionedData
{
public int Version { get; set; } = 1;
public DateTime SaveTime { get; set; }
// 玩家数据
public int HighScore { get; set; }
public int TotalKills { get; set; }
public float TotalPlayTime { get; set; }
// 设置
public float MusicVolume { get; set; } = 0.8f;
public float SfxVolume { get; set; } = 1.0f;
}
}
```
### 2.3 创建游戏架构
```csharp
// Scripts/Architecture/GameArchitecture.cs
using GFramework.Godot.architecture;
using GFramework.Core.Abstractions.architecture;
using GFramework.Game.Abstractions.data;
using GFramework.Game.Abstractions.storage;
using GFramework.Game.data;
using GFramework.Game.storage;
using MyShooterGame.Models;
using MyShooterGame.Systems;
using MyShooterGame.Data;
using Godot;
namespace MyShooterGame.Architecture
{
public class GameArchitecture : AbstractArchitecture
{
public static GameArchitecture Interface { get; private set; }
public GameArchitecture()
{
Interface = this;
}
protected override void InstallModules()
{
GD.Print("=== 初始化游戏架构 ===");
// 注册存储系统
var storage = new FileStorage("user://saves");
RegisterUtility(storage);
// 注册存档仓库
var saveConfig = new SaveConfiguration
{
SaveRoot = "",
SaveSlotPrefix = "save_",
SaveFileName = "data.json"
};
var saveRepo = new SaveRepository(storage, saveConfig);
RegisterUtility>(saveRepo);
// 注册 Model
RegisterModel(new PlayerModel());
RegisterModel(new GameModel());
// 注册 System
RegisterSystem(new GameplaySystem());
RegisterSystem(new SpawnSystem());
GD.Print("游戏架构初始化完成");
}
}
}
```
**代码说明**:
- `PlayerModel` 管理玩家状态
- `GameModel` 管理游戏状态
- `GameSaveData` 定义存档结构
- `GameArchitecture` 注册所有组件
## 步骤 3:实现游戏系统
创建游戏逻辑系统。
### 3.1 游戏逻辑系统
```csharp
// Scripts/Systems/GameplaySystem.cs
using GFramework.Core.system;
using GFramework.Core.extensions;
using MyShooterGame.Models;
using Godot;
namespace MyShooterGame.Systems
{
public class GameplaySystem : AbstractSystem
{
public void StartNewGame()
{
GD.Print("开始新游戏");
var playerModel = this.GetModel();
var gameModel = this.GetModel();
// 重置数据
playerModel.Reset();
gameModel.StartGame();
}
public void GameOver()
{
GD.Print("游戏结束");
var gameModel = this.GetModel();
gameModel.EndGame();
// 保存最高分
SaveHighScore();
}
public void PauseGame()
{
var gameModel = this.GetModel();
gameModel.PauseGame();
GetTree().Paused = true;
}
public void ResumeGame()
{
var gameModel = this.GetModel();
gameModel.ResumeGame();
GetTree().Paused = false;
}
private void SaveHighScore()
{
// 实现最高分保存逻辑
}
private SceneTree GetTree()
{
return (SceneTree)Engine.GetMainLoop();
}
}
}
```
### 3.2 敌人生成系统
```csharp
// Scripts/Systems/SpawnSystem.cs
using GFramework.Core.system;
using GFramework.Core.extensions;
using GFramework.Core.Abstractions.coroutine;
using GFramework.Core.coroutine;
using MyShooterGame.Models;
using Godot;
using System.Collections.Generic;
namespace MyShooterGame.Systems
{
public class SpawnSystem : AbstractSystem
{
private PackedScene _enemyScene;
private Node2D _spawnRoot;
private CoroutineHandle? _spawnCoroutine;
public void Initialize(Node2D spawnRoot, PackedScene enemyScene)
{
_spawnRoot = spawnRoot;
_enemyScene = enemyScene;
}
public void StartSpawning()
{
if (_spawnCoroutine.HasValue)
{
this.StopCoroutine(_spawnCoroutine.Value);
}
_spawnCoroutine = this.StartCoroutine(SpawnEnemiesCoroutine());
}
public void StopSpawning()
{
if (_spawnCoroutine.HasValue)
{
this.StopCoroutine(_spawnCoroutine.Value);
_spawnCoroutine = null;
}
}
private IEnumerator SpawnEnemiesCoroutine()
{
var gameModel = this.GetModel();
while (gameModel.IsPlaying.Value)
{
// 等待 2 秒
yield return CoroutineHelper.WaitForSeconds(2.0);
// 生成敌人
if (!gameModel.IsPaused.Value)
{
SpawnEnemy();
}
}
}
private void SpawnEnemy()
{
if (_enemyScene == null || _spawnRoot == null)
return;
var enemy = _enemyScene.Instantiate();
_spawnRoot.AddChild(enemy);
// 随机位置
var random = new Random();
enemy.Position = new Vector2(
random.Next(100, 900),
-50
);
var gameModel = this.GetModel();
gameModel.EnemiesAlive.Value++;
GD.Print($"生成敌人,当前数量: {gameModel.EnemiesAlive.Value}");
}
}
}
```
**代码说明**:
- `GameplaySystem` 管理游戏流程
- `SpawnSystem` 使用协程定时生成敌人
- 系统之间通过 Model 共享数据
## 步骤 4:创建玩家控制器
实现玩家的移动和射击。
```csharp
// Scripts/Controllers/PlayerController.cs
using GFramework.Core.Abstractions.controller;
using GFramework.Core.extensions;
using GFramework.SourceGenerators.Abstractions.rule;
using MyShooterGame.Architecture;
using MyShooterGame.Models;
using Godot;
namespace MyShooterGame.Controllers
{
[ContextAware]
public partial class PlayerController : CharacterBody2D, IController
{
[Export] public float Speed = 300f;
[Export] public PackedScene BulletScene;
private float _shootCooldown = 0f;
private const float ShootInterval = 0.2f;
public override void _Ready()
{
// 监听玩家死亡(使用扩展方法访问架构([ContextAware] 实现 IContextAware 接口))
var playerModel = this.GetModel();
playerModel.IsAlive.RegisterOnValueChanged(isAlive =>
{
if (!isAlive)
{
OnPlayerDied();
}
});
}
public override void _Process(double delta)
{
_shootCooldown -= (float)delta;
// 射击
if (Input.IsActionPressed("shoot") && _shootCooldown <= 0)
{
Shoot();
_shootCooldown = ShootInterval;
}
}
public override void _PhysicsProcess(double delta)
{
// 移动
var velocity = Vector2.Zero;
if (Input.IsActionPressed("move_left"))
velocity.X -= 1;
if (Input.IsActionPressed("move_right"))
velocity.X += 1;
if (Input.IsActionPressed("move_up"))
velocity.Y -= 1;
if (Input.IsActionPressed("move_down"))
velocity.Y += 1;
Velocity = velocity.Normalized() * Speed;
MoveAndSlide();
// 限制在屏幕内
var screenSize = GetViewportRect().Size;
Position = new Vector2(
Mathf.Clamp(Position.X, 0, screenSize.X),
Mathf.Clamp(Position.Y, 0, screenSize.Y)
);
}
private void Shoot()
{
if (BulletScene == null)
return;
var bullet = BulletScene.Instantiate();
GetParent().AddChild(bullet);
bullet.GlobalPosition = GlobalPosition + new Vector2(0, -20);
GD.Print("发射子弹");
}
public void TakeDamage(int damage)
{
var playerModel = this.GetModel();
playerModel.TakeDamage(damage);
GD.Print($"玩家受伤,剩余生命: {playerModel.Health.Value}");
}
private void OnPlayerDied()
{
GD.Print("玩家死亡");
var playerModel = this.GetModel();
playerModel.LoseLife();
if (playerModel.Lives.Value > 0)
{
// 重生
Position = new Vector2(400, 500);
}
else
{
// 游戏结束
var gameplaySystem = this.GetSystem();
gameplaySystem.GameOver();
}
}
}
}
```
**代码说明**:
- 实现 `IController` 接口访问架构
- 使用 Godot 的输入系统
- 通过 Model 更新游戏状态
- 监听属性变化响应事件
## 步骤 5:创建游戏场景
### 5.1 主场景 (Main.tscn)
创建主场景并添加架构初始化脚本:
```csharp
// Scripts/Main.cs
using Godot;
using MyShooterGame.Architecture;
public partial class Main : Node
{
private GameArchitecture _architecture;
public override async void _Ready()
{
GD.Print("初始化游戏");
// 创建并初始化架构
_architecture = new GameArchitecture();
await _architecture.InitializeAsync();
GD.Print("架构初始化完成,切换到菜单");
// 加载菜单场景
GetTree().ChangeSceneToFile("res://Scenes/Menu.tscn");
}
}
```
### 5.2 菜单场景 (Menu.tscn)
创建菜单UI并添加控制脚本:
```csharp
// Scripts/UI/MenuController.cs
using Godot;
using GFramework.Core.Abstractions.controller;
using GFramework.Core.extensions;
using GFramework.SourceGenerators.Abstractions.rule;
using MyShooterGame.Architecture;
using MyShooterGame.Systems;
[ContextAware]
public partial class MenuController : Control, IController
{
public override void _Ready()
{
// 连接按钮信号
GetNode