# Godot 遊戲開發 學習筆記
[toc]

## Godot 編輯器
### Parallax Background 視差背景
1. 新增 **ParallaxBackground** Node, 在子節點中新增 **ParallaxLayer**。
2. 把圖片拖到 **ParallaxLayer** 子節點中。
3. 把 Offset>Centered -> off,and Reset Position
4. 在 **ParallaxLayer** 中,修改 Motion>Scale 的值調整視差距離。
## 程式(C#)
### Player
#### Gravity
```csharp
private float _defaultGravity =
ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle();
```
設定重力
#### Move
```csharp
public override void _PhysicsProcess(double delta)
{
// Input
float _direction = Input.GetAxis("move_left", "move_right");
// Run
float _acceleration = IsOnFloor() ? FLOOR_ACCELERATION : AIR_ACCELERATION;
Velocity = new Vector2(Mathf.MoveToward(Velocity.X, _direction * RUN_SPEED, _acceleration * (float)delta),
Velocity.Y + _gravity * (float)delta);
}
```
**void _PhysicsProcess(double delta)** :
物理方面的Update
**Mathf.MoveToward(from, to, delta)** :
把當前v的量值(from) 改變到結束值(to), 期間花(delta)秒。
> 因為是在**PhysicsProcess**裡執行, 所以不管是加速還是減速都會有緩衝的效果

#### Jump
```csharp
//...
public override void _PhysicsProcess(double delta)
{
// Jump
_canJump = IsOnFloor() || _coyoteTimer.TimeLeft > 0;
_isInputJump = _canJump && _jumpRequestTimer.TimeLeft > 0;
if (_isInputJump)
{
Velocity = new Vector2(Velocity.X, -JUMP_SPEED);
_coyoteTimer.Stop();
_jumpRequestTimer.Stop();
}
//...
_isOnFloorBeforeMove = IsOnFloor();
MoveAndSlide();
// Coyote time
// (Can jump after falling off a platform)
if (IsOnFloor() != _isOnFloorBeforeMove)
{
if (_isOnFloorBeforeMove && !_isInputJump)
_coyoteTimer.Start();
else
_coyoteTimer.Stop();
}
}
```
#### Animation
```csharp
public override void _PhysicsProcess(double delta)
{
// Animation
if (IsOnFloor())
{
_animationPlayer.Play(Mathf.IsZeroApprox(_direction) && Mathf.IsZeroApprox(Velocity.X) ?
"idle" : "running");
}
else
{
_animationPlayer.Play("jump");
}
// Flip sprite
if(_direction != 0)
_sprite.FlipH = _direction < 0;
}
```
**Mathf.IsZeroApprox(float)** :
當參數的值(float) **接近或是等於0時**,此函數會返回**true**
**[_animationPlayer].Play("name")** :
播放動畫("name")
### State Machine
#### StateMachine.cs (Base)
```csharp
using Godot;
using System;
public partial class StateMachine : Node
{
private int currentState = 1; // Use int to replace the enum ,
// because the other state enum name is not different
public int CurrentState
{
get => currentState;
set
{
currentState = value;
GetParent<CharacterController>().TransitionToState((State)currentState, (State)value);
}
}
public override async void _Ready()
{
await ToSignal(Owner, "ready");
CurrentState = 0;
}
public override void _PhysicsProcess(double delta)
{
int nextState = (int)GetParent<CharacterController>().GetNextState((State)CurrentState);
if (nextState != CurrentState)
{
CurrentState = nextState;
}
GetParent<CharacterController>().TickPhysics((State)CurrentState, delta);
}
}
```
**GetParent\<T\>()** :
得到父類的T組件 (GodotScript是用 "owner")
**await**:
像是Unity的 yield return
**ToSignal(Object, signalName)**:
確定物件的基本函數已經執行
#### Player狀態機 架構
```csharp
public enum State
{
IDLE //...
}
public partial class PlayerController : CharacterController
{
/// <summary>
/// What do you do in this frame by State?
/// </summary>
/// <param name="state"></param>
/// <param name="delta"></param>
public override void TickPhysics(State state, double delta)
{
switch (state)
{
case State.IDLE:
// doing Physics (like move)
break;
//...
}
}
/// <summary>
/// Can to change State?
/// </summary>
/// <param name="state"></param>
/// <returns></returns>
public override State GetNextState(State state)
{
switch(state)
{
case State.IDLE:
// Check to let 'State.IDLE' to other state
break;
//...
}
}
/// <summary>
/// What do you do when you change State? (a frame)
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
public override void TransitionToState(State from, State to)
{
switch(to)
{
case State.IDLE:
// do something (like animation)
break;
//...
}
}
}
```
#### 小技巧 (抽象類)
```csharp
using Godot;
using System;
public abstract partial class CharacterController : CharacterBody2D
{
public abstract void TickPhysics(State state, double delta);
public abstract State GetNextState(State state);
public abstract void TransitionToState(State from, State to);
}
```
讓所有角色類Script都繼承 "CharacterController", 強制讓他們都必須要Override那三個函式