---
# System prepended metadata

title: Godot 遊戲開發 學習筆記
tags: [Godot]

---

# Godot 遊戲開發 學習筆記

[toc]

![](https://hackmd.io/_uploads/ryWC5JQJa.png)
## 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**裡執行， 所以不管是加速還是減速都會有緩衝的效果

![](https://imgur.com/3YAr8zw.png)

#### 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那三個函式