Unity教程(二十三)技能系統 投劍技能(上)基礎實現

Unity開發2D類銀河惡魔城游戲學習筆記

Unity教程(零)Unity和VS的使用相關內容
Unity教程(一)開始學習狀態機
Unity教程(二)角色移動的實現
Unity教程(三)角色跳躍的實現
Unity教程(四)碰撞檢測
Unity教程(五)角色沖刺的實現
Unity教程(六)角色滑墻的實現
Unity教程(七)角色蹬墻跳的實現
Unity教程(八)角色攻擊的基本實現
Unity教程(九)角色攻擊的改進

Unity教程(十)Tile Palette搭建平臺關卡
Unity教程(十一)相機
Unity教程(十二)視差背景

Unity教程(十三)敵人狀態機
Unity教程(十四)敵人空閑和移動的實現
Unity教程(十五)敵人戰斗狀態的實現
Unity教程(十六)敵人攻擊狀態的實現
Unity教程(十七)敵人戰斗狀態的完善

Unity教程(十八)戰斗系統 攻擊邏輯
Unity教程(十九)戰斗系統 受擊反饋
Unity教程(二十)戰斗系統 角色反擊

Unity教程(二十一)技能系統 基礎部分
Unity教程(二十二)技能系統 分身技能
Unity教程(二十三)技能系統 擲劍技能(上)基礎實現
Unity教程(二十四)技能系統 擲劍技能(中)技能變種實現


如果你更習慣用知乎
Unity開發2D類銀河惡魔城游戲學習筆記目錄


文章目錄

  • Unity開發2D類銀河惡魔城游戲學習筆記
  • 前言
  • 一、概述
  • 二、投劍相關狀態的創建與實現
    • (1)創建PlayerAimSwordState和PlayerCatchSwordState
    • (2)創建瞄準與接劍的動畫
    • (3)實現瞄準投擲的狀態轉換
  • 三、投劍技能的創建與實現
    • (1)創建Sword物體
    • (2)創建Sword閑置和旋轉動畫
    • (3)創建投劍技能及控制器腳本
    • (4)投擲劍的實現
  • 四、瞄準的實現
    • (1)瞄準的實現
    • (2)顯示瞄準點和路徑
    • (3)劍的旋轉與嵌入
    • (4)解決投劍方向的問題
    • (5)瞄準投擲時滑動問題的解決
  • 五、接劍的實現
    • (1)劍的回收
    • (2)改進接劍方向
    • (3)接劍狀態的轉換
  • 總結 完整代碼
    • Player.cs
    • PlayerAimSwordState.cs
    • PlayerCatchSwordState.cs
    • PlayerGroundedState.cs
    • PlayerAnimationTriggers.cs
    • Sword_Skill.cs
    • Sword_Skill_Controller.cs
    • SkillManager.cs


前言

本文為Udemy課程The Ultimate Guide to Creating an RPG Game in Unity學習筆記,如有錯誤,歡迎指正。

本節實現角色投劍技能。

Udemy課程地址

對應視頻:
Sword Throw Skill State
Setting up details of the sword
Setting up sword’s aim
Improving sword’s behaviour
Improving sword throwing state


一、概述

本節實現投劍技能。
實現投劍的基本功能部分,包括創建動畫、狀態轉換和劍的預制體創建和投出。
實現瞄準的基本功能,包括瞄準狀態轉換、瞄準軌跡的顯示、劍的旋轉和嵌入、瞄準方向等。
實現接劍的基本功能,包括保證劍的唯一性、接劍狀態轉換和接劍的反饋效果等。
新創建瞄準投劍和接劍兩個狀態,具體狀態轉換如下:
在這里插入圖片描述
主要具體實現如下:
在這里插入圖片描述

二、投劍相關狀態的創建與實現

(1)創建PlayerAimSwordState和PlayerCatchSwordState

創建腳本瞄準狀態PlayerAimSwordState和接劍狀態PlayerCatchSwordState,它們繼承自PlayerState。

在這里插入圖片描述
Alt+Enter生成構造函數和重寫。

在這里插入圖片描述
在Player中聲明兩個狀態。

    #region 狀態public PlayerStateMachine StateMachine { get; private set; }public PlayerIdleState idleState { get; private set; }public PlayerMoveState moveState { get; private set; }public PlayerJumpState jumpState { get; private set; }public PlayerAirState airState { get; private set; }public PlayerDashState dashState { get; private set; }public PlayerWallSlideState wallSlideState { get; private set; }public PlayerWallJumpState wallJumpState { get; private set; }public PlayerPrimaryAttackState primaryAttack { get; private set; }public PlayerCounterAttackState counterAttack { get; private set; }public PlayerAimSwordState aimSword { get; private set; }public PlayerCatchSwordState catchSword {  get; private set; }#endregion//創建對象protected override void Awake(){base.Awake();StateMachine = new PlayerStateMachine();idleState = new PlayerIdleState(StateMachine, this, "Idle");moveState = new PlayerMoveState(StateMachine, this, "Move");jumpState = new PlayerJumpState(StateMachine, this, "Jump");airState = new PlayerAirState(StateMachine, this, "Jump");dashState = new PlayerDashState(StateMachine, this, "Dash");wallSlideState = new PlayerWallSlideState(StateMachine, this, "WallSlide");wallJumpState = new PlayerWallJumpState(StateMachine, this, "Jump");primaryAttack = new PlayerPrimaryAttackState(StateMachine, this, "Attack");counterAttack = new PlayerCounterAttackState(StateMachine, this, "CounterAttack");aimSword = new PlayerAimSwordState(StateMachine, this, "AimSword");catchSword = new PlayerCatchSwordState(StateMachine, this, "CatchSword");}

(2)創建瞄準與接劍的動畫

瞄準與接劍相關動畫在Sword+Aim+Throw這張精靈表中
在這里插入圖片描述
層次面板中選中Animator,在Animation面板中創建動畫playerAimSword
playerAimSword精靈表標號14-16,采樣率改為15
具體講解見Unity教程(零)Unity和VS的使用相關內容

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

同理,playerThrowSword,精靈表標號16、17、21、22,采樣率改為14

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

同理,playerCatchSword,精靈表標號17、16、15、14,采樣率改為14
在這里插入圖片描述
在這里插入圖片描述
取消三個動畫的循環時間
在這里插入圖片描述
連接狀態機。
連接playerAimSword和PlayerThrowSword
添加過渡條件AimSword,并修改過渡設置
在這里插入圖片描述
Entry->playerAimSword的過渡,加條件變量
在這里插入圖片描述
playerAimSword->playerThrowSword的過渡,加條件變量
在這里插入圖片描述
playerThrowSword->Exit的過渡,修改退出時間和持續時間
在這里插入圖片描述
連接playerCatchSword
添加過渡條件CatchSword,并修改過渡設置
在這里插入圖片描述
Entry->playerCatchSword的過渡,加條件變量
在這里插入圖片描述
playerCatchSword->Exit的過渡,加條件變量
在這里插入圖片描述

(3)實現瞄準投擲的狀態轉換

要實現的功能為按下鼠標右鍵玩家進入瞄準狀態,松開鼠標右鍵玩家恢復空閑狀態。
在playerGroundedState玩家接地狀態中添加轉換到playerAimSwordState的代碼。

    public override void Update(){base.Update();if (Input.GetKeyDown(KeyCode.Mouse1))stateMachine.ChangeState(player.aimSword);if (Input.GetKeyDown(KeyCode.Q))stateMachine.ChangeState(player.counterAttack);if (Input.GetKeyDown(KeyCode.Mouse0))stateMachine.ChangeState(player.primaryAttack);if(!player.isGroundDetected())stateMachine.ChangeState(player.airState);if (Input.GetKeyDown(KeyCode.Space)&& player.isGroundDetected())stateMachine.ChangeState(player.jumpState);}

在playerAimSwordState中退出狀態。

    public override void Update(){base.Update();if(Input.GetKeyUp(KeyCode.Mouse1))stateMachine.ChangeState(player.idleState);}

效果如下:
在這里插入圖片描述
在這里插入圖片描述

三、投劍技能的創建與實現

(1)創建Sword物體

劍的精靈表是SwordSpin這一張。
在這里插入圖片描述

我們拖一張出來到層次面板中重命名為Sword。
修改劍的層次。
在這里插入圖片描述
我們還是把它掛在一個空物體下面,方便添加組件。
右擊->Create Empty Parent->重命名為Sword
在這里插入圖片描述

在父物體上添加剛體和圓形碰撞器
在這里插入圖片描述

(2)創建Sword閑置和旋轉動畫

創建動畫控制器Sword_AC,把它掛在子物體Sword上。

在這里插入圖片描述
在這里插入圖片描述
在Animations文件夾中,新建Sword文件夾存放劍的閑置動畫和旋轉動畫。
在Animation面板中創建動畫SwordIdle
SwordIdle精靈表標號23
在這里插入圖片描述
在這里插入圖片描述
在Animation面板中創建動畫SwordFlip
SwordFlip精靈表標號3、5、9、12,采樣率改為20
在這里插入圖片描述
在這里插入圖片描述
把swordFlip設置為默認狀態
在這里插入圖片描述
連接狀態機。
連接SwordFlip和SwordIdle。
添加過渡條件Rotation,并修改過渡設置。
在這里插入圖片描述
SwordFlip->SwordIdle的過渡,加條件變量
在這里插入圖片描述
SwordIdle->SwordFlip的過渡,加條件變量
在這里插入圖片描述

(3)創建投劍技能及控制器腳本

創建投劍技能腳本Sword_Skill,它繼承自Skill技能基類。
在這里插入圖片描述
在投劍技能腳本中添加經常修改的技能信息。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Sword_Skill : Skill
{[Header("Skill Info")][SerializeField] private GameObject swordPrefab;[SerializeField] private Vector2 launchForce;[SerializeField] private float swordGravity;
}

在SkillManager中創建技能。

    public Dash_Skill dash { get; private set; }public Clone_Skill clone { get; private set; }public Sword_Skill sword { get; private set; }private void Start(){dash = GetComponent<Dash_Skill>();clone = GetComponent<Clone_Skill>();sword = GetComponent<Sword_Skill>();}

創建控制器腳本Sword_Skill_Controller。
在這里插入圖片描述
在Sword_Skill_Controller中添加變量獲取要用的組件。

//Sword_Skill_Controller:擲劍技能控制器
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Sword_Skill_Controller : MonoBehaviour
{private Animator anim;private Rigidbody2D rb;private CircleCollider2D cd;private Player player;private void Awake(){anim = GetComponentInChildren<Animator>();rb = GetComponent<Rigidbody2D>();cd = GetComponent<CircleCollider2D>();}
}

技能實現依然類似上節的分身技能,預制體的創建與設置分別在技能腳本和控制器腳本中實現。

在控制器腳本中添加參數設置。

    public void SetupSword(Vector2 _dir, float _gravityScale, Player _player){player = _player;rb.velocity = _dir;rb.gravityScale = _gravityScale;}

在技能腳本中實例化Sword預制體到角色現在的位置。
這要用到player的位置參數,先在Skill基類中創建player變量并賦值,方便后續書寫。

    protected Player player;protected virtual void Start(){player = PlayerManager.instance.player;}

在Sword_Skill中添加函數CreateSword,實例化預制體,并調用控制器中的函數SetupSword設置參數。

    public void CreateSword(){GameObject newSword = Instantiate(swordPrefab, player.transform.position, transform.rotation);Sword_Skill_Controller newSwordScript = newSword.GetComponent<Sword_Skill_Controller>();newSwordScript.SetupSword(launchForce, swordGravity,player);   }

將Sword_Skill掛在SkillManager下。
在這里插入圖片描述

將Sword_Skill_Controller掛到創建的物體Sword下。
在這里插入圖片描述

將Sword拉成預制體。
為Sword_Skill的參數賦值,從預制體文件夾中拖入劍的預制體,為參數賦一個合適的值
在這里插入圖片描述

(4)投擲劍的實現

在使用技能時,我們要讓角色扔出劍,這里要用Animator的事件實現。
在玩家觸發器腳本PlayerAnimationTriggers中添加調用CreateSword的函數。

    private void ThrowSword(){SkillManager.instance.sword.CreateSword();}

在playerThrowSword動畫中添加事件,觸發事件時創建Sword。
在這里插入圖片描述
在這里插入圖片描述
效果如下:
在這里插入圖片描述

四、瞄準的實現

(1)瞄準的實現

現在已經實現了將劍投擲出去,但作為技能使用,我們希望劍沿著鼠標所指的方向投擲。

實現這一部分需要獲取鼠標的世界坐標。Input.mousePosition可以獲得鼠標的屏幕坐標,我們將它轉換到世界坐標后計算方向。最終的投擲方向由以玩家位置為起點、鼠標位置為終點的向量和投擲力度計算得到。
在Sword_Skill中添加函數AimDirection獲取瞄準的方向。

    public Vector2 AimDirection(){Vector2 playerPosition = player.transform.position;Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);Vector2 direction = mousePosition - playerPosition;return direction;}

這里用到了函數Camera.ScreenToWorldPoint,它可以把點從屏幕空間變換到三維空間。
它的介紹見 Unity官方手冊


創建變量finalDir存放最終的方向,并在Update函數中不斷計算更新方向。計算時,將瞄準方向歸一化后,乘該方向的投擲力度獲得最終方向。將CreateSword中的投擲方向改為finalDir。

    private Vector2 finalDir;protected override void Update(){if(Input.GetKeyUp(KeyCode.Mouse1))finalDir = new Vector2(AimDirection().normalized.x * launchForce.x , AimDirection().normalized.y * launchForce.y);}public void CreateSword(){GameObject newSword = Instantiate(swordPrefab, player.transform.position, transform.rotation);Sword_Skill_Controller newSwordScript = newSword.GetComponent<Sword_Skill_Controller>();newSwordScript.SetupSword(finalDir, swordGravity, player);   }

(2)顯示瞄準點和路徑

先把瞄準點都生成在玩家位置,但是先不激活不顯示。直到按下鼠標右鍵進入瞄準狀態,再激活瞄準點并計算修改點的位置。在將劍實例化后就可以取消點的顯示,再次設置為未激活狀態了。

劍被拋出去后運動路線是個拋物線,通過運動公式計算間隔一定時刻劍的位移來確定位置。設置點的個數和間隔控制瞄準點的顯示。

在Sword_Skill腳本中添加瞄準點相關變量。

    [Header("Aim dots")][SerializeField] private int numberOfDots;[SerializeField] private float spaceBetweenDots;[SerializeField] private GameObject dotPrefab;[SerializeField] private Transform dotsParent;private GameObject[] dots;

先創建生成瞄準點的函數,實例化預制體存儲在數組中,將瞄準點改為未激活狀態。
創建激活瞄準點的函數方便使用。
生成與激活瞄準點的函數如下:

    protected override void Start(){base.Start();GenerateDots();}private void GenerateDots(){dots = new GameObject[numberOfDots];for (int i = 0; i < numberOfDots; i++){dots[i] = Instantiate(dotPrefab, player.transform.position, Quaternion.identity, dotsParent);dots[i].SetActive(false);}}public void DotsActive(bool _isActive){for (int i = 0; i < dots.Length; i++){dots[i].SetActive(_isActive);}}

顯示瞄準點要根據時間變化計算軌跡上點的位置,將t作為參數傳入,采用簡單的運動公式計算軌跡上的點
{ x = v x t , y = v y t + 1 2 g t 2 \begin{cases} x = v_x t, \\ y = v_y t + \frac{1}{2} g t^2 \end{cases} {x=vx?t,y=vy?t+21?gt2?
調用計算函數,每隔固定時刻取一個點,顯示整條軌跡。函數將在Update函數中調用,在鼠標右鍵被按住時,隨鼠標運動改變瞄準點顯示。
注意:這里是按著鼠標時,要用GetKey()

代碼如下:

    protected override void Update(){if(Input.GetKeyUp(KeyCode.Mouse1))finalDir = new Vector2(AimDirection().normalized.x * launchForce.x , AimDirection().normalized.y * launchForce.y);if(Input.GetKey(KeyCode.Mouse1)){for(int i = 0; i < dots.Length; i++){dots[i].transform.position = DotsPosition(i * spaceBetweenDots);}}}private Vector2 DotsPosition(float t){Vector2 position = (Vector2)player.transform.position +new Vector2(AimDirection().normalized.x * launchForce.x, AimDirection().normalized.y * launchForce.y) * t +0.5f * (Physics2D.gravity * swordGravity) * (t * t);return position;}

在PlayerAimSwordState的Enter函數中添加瞄準點的激活

    public override void Enter(){base.Enter();player.skill.sword.DotsActive(true);}

在SwordSkill的CreateSword函數中設置完參數劍要丟出了,這時就可以關閉瞄準點把它改為未激活了。

    public void CreateSword(){GameObject newSword = Instantiate(swordPrefab, player.transform.position, transform.rotation);Sword_Skill_Controller newSwordScript = newSword.GetComponent<Sword_Skill_Controller>();newSwordScript.SetupSword(finalDir, swordGravity, player);   DotsActive(false);}

創建瞄準點預制體。
創建一個圓形精靈,改變它的層次為地面層 -2,使它在地里面不可見。
右鍵 -> 2D Object -> Sprite ->Circle ->重命名為AimDot
在這里插入圖片描述
在這里插入圖片描述

修改它的大小、顏色到合適的值
在這里插入圖片描述
拉成預制體
在這里插入圖片描述
在Player上創建空物體AimDotParent作為瞄準點的父物體。
在這里插入圖片描述

在技能管理器上給Sword_Skill參數賦值
在這里插入圖片描述

效果如下:
在這里插入圖片描述

(3)劍的旋轉與嵌入

可以看到,現在在投擲出去后,劍是完全不動的,而且會砸在骷髏身上彈回來,我們需要把它改的更符合規律一點。

讓劍尖順著投擲的軌跡旋轉直到擊中骷髏。我們可以通過修改坐標軸的方向實現,這里選取了x軸,讓坐標軸與運動方向一致,劍也會隨之旋轉。
首先要把劍調整為與x軸平齊,劍尖朝向x軸正方向,劍才能沿著軌跡運動。調整時旋轉子物體,把它朝向右,到以下程度:
在這里插入圖片描述
在這里插入圖片描述
注意:記得應用預制體的更改

在這里插入圖片描述
在Sword_Skill_Controller中添加update函數,根據速度實時改變坐標軸方向

    private void Update(){transform.right = rb.velocity;}

在這里插入圖片描述

接下來實現讓劍嵌入敵人和地面,在預制體面板中進行更改。
在碰撞盒上勾選觸發器。
在這里插入圖片描述

我們要讓劍在插入物體后靜止,要將剛體類型改為Kinematic,Kinematic 2D 剛體不受重力和作用力影響且開銷較小。
更詳細的介紹請見 Unity官方手冊 2D剛體
同時鎖定XYZ三個軸的旋轉和移動避免劍產生多余的運動,關閉碰撞器避免產生連續碰撞。

在Sword_Skill_Controller中添加參數canRotate來表示是否處于旋轉,默認值設為true。只有處于旋轉時需要改變劍的坐標軸。
創建觸發器函數,在有物體進入劍的觸發器時,將canRotate改為false,并實行以上操作讓劍靜止插入物體,同時將劍作為子物體掛在插入的物體上,讓劍后續跟隨物體一起移動。

    private bool canRotate = true;private void Update(){if (canRotate){transform.right = rb.velocity;}}private void OnTriggerEnter2D(Collider2D collision){canRotate = false;cd.enabled = false;rb.isKinematic = true;rb.constraints = RigidbodyConstraints2D.FreezeAll;transform.parent = collision.transform;}

現在投擲劍,劍會與玩家碰撞,粘在玩家身上,我們要更改劍的圖層來解決這一問題。
添加Sword圖層,將劍及其子物體都改為Sword層
在這里插入圖片描述在這里插入圖片描述
在這里插入圖片描述

修改碰撞矩陣
Edit -> Project Setting -> Physics 2D -> Layer Collision Matrix -> 僅保留Sword與Ground和Enemy的碰撞

在這里插入圖片描述
在這里插入圖片描述
效果如下
在這里插入圖片描述

(4)解決投劍方向的問題

在這里插入圖片描述

角色不會隨投擲方向的變化隨之改變面朝的方向,要修改代碼使角色隨鼠標位置改變面向。
改變朝向的條件如下:

鼠標在角色左側,角色面向右
鼠標在角色右側,角色面向左

在PlayerAimSwordState中添加:

    public override void Update(){base.Update();if(Input.GetKeyUp(KeyCode.Mouse1))stateMachine.ChangeState(player.idleState);Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);if (player.transform.position.x > mousePosition.x && player.facingDir == 1)player.Flip();else if(player.transform.position.x < mousePosition.x && player.facingDir == -1)player.Flip();}

效果如下:
在這里插入圖片描述

(5)瞄準投擲時滑動問題的解決

在這里插入圖片描述
我們要參照之前空閑狀態寫的,在瞄準和接劍狀態退出時要調用協程使它處于"忙的狀態"
在這里插入圖片描述
玩家從移動狀態轉換到瞄準狀態時也依然會滑動,因此在瞄準狀態中將速度設置為0。
在瞄準和接劍狀態里分別添加

//PlayerAimSwordState: 玩家瞄準狀態public override void Exit(){base.Exit();player.StartCoroutine("BusyFor", 0.2f);}public override void Update(){base.Update();player.ZeroVelocity();if (Input.GetKeyUp(KeyCode.Mouse1))stateMachine.ChangeState(player.idleState);Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);if (player.transform.position.x > mousePosition.x && player.facingDir == 1)player.Flip();else if(player.transform.position.x < mousePosition.x && player.facingDir == -1)player.Flip();}
//PlayerCatchSwordState: 玩家接劍狀態public override void Exit(){base.Exit();player.StartCoroutine("BusyFor", 0.1f);}

五、接劍的實現

(1)劍的回收

現在可以同時存在多把劍,于是可能出現下面把骷髏扎成刺猬的場景,我們需要改進這一點,讓場景中只能同時存在一把劍,劍擊中敵人后可以回收。
在這里插入圖片描述
添加變量sword,每次創建后都分配新的劍給此變量
在player中添加變量sword,并添加分配和接住sword的函數。接劍時將轉到PlayerCatchSwordState。

public SkillManager skill{get; private set;}public GameObject sword { get; private set; }public void AssignNewSword(GameObject _newSword){sword = _newSword;}public void ClearTheSword(){Destroy(sword);}

在創建劍后,將劍分配給sword,在Sword_Skill中調用函數

    public void CreateSword(){GameObject newSword = Instantiate(swordPrefab, player.transform.position, transform.rotation);Sword_Skill_Controller newSwordScript = newSword.GetComponent<Sword_Skill_Controller>();newSwordScript.SetupSword(finalDir, swordGravity, player);player.AssignNewSword(newSword);DotsActive(false);}

接下來實現劍的返回。
在Sword_Skill中添加返回速度returnSpeed,傳遞到控制器中。

    [Header("Skill Info")][SerializeField] private GameObject swordPrefab;[SerializeField] private Vector2 launchForce;[SerializeField] private float swordGravity;[SerializeField] private float returnSpeed;public void CreateSword(){GameObject newSword = Instantiate(swordPrefab, player.transform.position, transform.rotation);Sword_Skill_Controller newSwordScript = newSword.GetComponent<Sword_Skill_Controller>();newSwordScript.SetupSword(finalDir, swordGravity, player, returnSpeed);player.AssignNewSword(newSword);DotsActive(false);}

在Sword_Skill_Controller中添加返回相關變量,isReturning和returnSpeed,分別表示是否返回中和返回速度。
創建ReturnSword函數,讓劍不再旋轉并且不再跟隨插入的物體移動,同時把isReturning改為true,表示正在收回劍。這里不使用改為Kinematic剛體的方式,因為這會使劍無法中途收回。
當劍處于收回狀態時,讓它以設定好的速度由當前位置向著玩家位置移動,當距離小于某一值時,銷毀劍,這一部分寫在Update中隨時間更新位置。

    private bool isReturning;private float returnSpeed = 12;public void SetupSword(Vector2 _dir, float _gravityScale, Player _player, float _returnSpeed){player = _player;rb.velocity = _dir;rb.gravityScale = _gravityScale;returnSpeed = _returnSpeed;}public void ReturnSword(){rb.constraints = RigidbodyConstraints2D.FreezeAll;transform .parent = null;isReturning = true;}private void Update(){if (canRotate){transform.right = rb.velocity;}if (isReturning){transform.position = Vector2.MoveTowards(transform.position, player.transform.position, returnSpeed * Time.deltaTime);if(Vector2.Distance(transform.position, player.transform.position) < 1)player.ClearTheSword();}}

修改進入瞄準狀態的條件,將它改為瞄準投劍和收回劍共用的操作。
按下鼠標右鍵且當player沒有被分配劍時轉換到瞄準狀態;如果有分配劍,則調用Sword_Skill_Controller中讓劍返回的函數ReturnSword()。寫個函數HasNoSword判斷并實現

    //更新public override void Update(){base.Update();if (Input.GetKeyDown(KeyCode.Mouse1) && HasNoSword())stateMachine.ChangeState(player.aimSword);if (Input.GetKeyDown(KeyCode.Q))stateMachine.ChangeState(player.counterAttack);if (Input.GetKeyDown(KeyCode.Mouse0))stateMachine.ChangeState(player.primaryAttack);if(!player.isGroundDetected())stateMachine.ChangeState(player.airState);if (Input.GetKeyDown(KeyCode.Space)&& player.isGroundDetected())stateMachine.ChangeState(player.jumpState);}private bool HasNoSword(){if (!player.sword){return true;}player.sword.GetComponent<Sword_Skill_Controller>().ReturnSword();return false;}

給回收速度賦一個合適的值
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
為了提升技能效果,在扔出劍時,播放劍的旋轉動畫;在劍接觸到物體碰撞時停止播放動畫。
在設置劍和觸發器函數中分別添加改變動畫變量Rotation的語句。

    public void SetupSword(Vector2 _dir, float _gravityScale, Player _player, float _returnSpeed){player = _player;rb.velocity = _dir;rb.gravityScale = _gravityScale;returnSpeed = _returnSpeed;anim.SetBool("Rotation", true);}private void OnTriggerEnter2D(Collider2D collision){anim.SetBool("Rotation", false);canRotate = false;cd.enabled = false;rb.isKinematic = true;rb.constraints = RigidbodyConstraints2D.FreezeAll;transform.parent = collision.transform;}}

在這里插入圖片描述

(2)改進接劍方向

接劍的方向和投擲劍的方向問題一樣,在PlayerCatchSwordState中獲取劍的位置,比較角色位置與劍的位置確定是否應該翻轉。

    public override void Enter(){base.Enter();sword = player.sword.transform;if (player.transform.position.x > sword.position.x && player.facingDir == 1)player.Flip();else if (player.transform.position.x < sword.position.x && player.facingDir == -1)player.Flip();}

(3)接劍狀態的轉換

前面在接劍時,我們直接調用ClearSword函數銷毀劍。除此之外,還會播放接劍動畫,讓我們添加這部分。
首先,Ctrl+R快捷鍵,將Player中的函數ClearSword改名為CatchSword。在函數中增加到PlayerCatchSwordState的狀態轉換。

    public void CatchTheSword(){StateMachine.ChangeState(catchSword);Destroy(sword);}

退出接劍狀態,要在playerCatchSword中觸發事件實現。
在動畫的最后一幀添加動畫事件,當觸發器被觸發時,轉換為空閑狀態。
在這里插入圖片描述
在這里插入圖片描述
在playerCatchSwordState中添加

    public override void Update(){base.Update();if (triggerCalled)stateMachine.ChangeState(player.idleState);}

在這里插入圖片描述
接劍的時候讓角色后退一點,顯示劍有一個小的沖擊力。
在Player中添加變量swordReturnImpact。

    [Header("Move Info")]public float moveSpeed = 8f;public float jumpForce = 12f;public float swordReturnImpact = 7f;

在接劍狀態中添加接劍后改變速度。

    public override void Enter(){base.Enter();sword = player.sword.transform;if (player.transform.position.x > sword.position.x && player.facingDir == 1)player.Flip();else if (player.transform.position.x < sword.position.x && player.facingDir == -1)player.Flip();rb.velocity = new Vector2 (player.swordReturnImpact * -player.facingDir,rb.velocity.y);}

賦上合適的值
在這里插入圖片描述
在這里插入圖片描述

總結 完整代碼

Player.cs

創建PlayerAimSwordState和PlayerCatchSwordState兩個狀態并賦值。
添加接劍時后退的相關參數swordReturnImpact。
添加參數sword獲取投擲的劍,并判斷現在是否有劍被創建分配。

//Player:玩家
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Player : Entity
{[Header("Attack details")]public Vector2[] attackMovement;public float counterAttackDuration = 0.2f;public bool isBusy { get; private set; }[Header("Move Info")]public float moveSpeed = 8f;public float jumpForce = 12f;public float swordReturnImpact = 7f;[Header("Dash Info")]public float dashSpeed=25f;public float dashDuration=0.2f;public float dashDir { get; private set; }public SkillManager skill{get; private set;}public GameObject sword { get; private set; }#region 狀態public PlayerStateMachine StateMachine { get; private set; }public PlayerIdleState idleState { get; private set; }public PlayerMoveState moveState { get; private set; }public PlayerJumpState jumpState { get; private set; }public PlayerAirState airState { get; private set; }public PlayerDashState dashState { get; private set; }public PlayerWallSlideState wallSlideState { get; private set; }public PlayerWallJumpState wallJumpState { get; private set; }public PlayerPrimaryAttackState primaryAttack { get; private set; }public PlayerCounterAttackState counterAttack { get; private set; }public PlayerAimSwordState aimSword { get; private set; }public PlayerCatchSwordState catchSword {  get; private set; }#endregion//創建對象protected override void Awake(){base.Awake();StateMachine = new PlayerStateMachine();idleState = new PlayerIdleState(StateMachine, this, "Idle");moveState = new PlayerMoveState(StateMachine, this, "Move");jumpState = new PlayerJumpState(StateMachine, this, "Jump");airState = new PlayerAirState(StateMachine, this, "Jump");dashState = new PlayerDashState(StateMachine, this, "Dash");wallSlideState = new PlayerWallSlideState(StateMachine, this, "WallSlide");wallJumpState = new PlayerWallJumpState(StateMachine, this, "Jump");primaryAttack = new PlayerPrimaryAttackState(StateMachine, this, "Attack");counterAttack = new PlayerCounterAttackState(StateMachine, this, "CounterAttack");aimSword = new PlayerAimSwordState(StateMachine, this, "AimSword");catchSword = new PlayerCatchSwordState(StateMachine, this, "CatchSword");}// 設置初始狀態protected override void Start(){base.Start();skill = SkillManager.instance;StateMachine.Initialize(idleState);}// 更新protected override void Update(){base.Update();StateMachine.currentState.Update();CheckForDashInput();}public void AssignNewSword(GameObject _newSword){sword = _newSword;}public void CatchTheSword(){StateMachine.ChangeState(catchSword);Destroy(sword);}public IEnumerator BusyFor(float _seconds){isBusy = true;yield return new WaitForSeconds(_seconds);isBusy = false;}//設置觸發器public void AnimationTrigger() => StateMachine.currentState.AnimationFinishTrigger();//檢查沖刺輸入public void CheckForDashInput(){if (Input.GetKeyDown(KeyCode.LeftShift) && SkillManager.instance.dash.CanUseSkill()){dashDir = Input.GetAxisRaw("Horizontal");if (dashDir == 0)dashDir = facingDir;StateMachine.ChangeState(dashState);}}}

PlayerAimSwordState.cs

激活瞄準點。
實現瞄準狀態到空閑狀態的轉換。
實現瞄準時根據鼠標位置左右翻轉。
設置處于忙碌中,防止滑動。

//PlayerAimSwordState: 玩家瞄準狀態
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayerAimSwordState : PlayerState
{public PlayerAimSwordState(PlayerStateMachine _stateMachine, Player _player, string _animBoolName) : base(_stateMachine, _player, _animBoolName){}public override void Enter(){base.Enter();player.skill.sword.DotsActive(true);}public override void Exit(){base.Exit();player.StartCoroutine("BusyFor", 0.2f);}public override void Update(){base.Update();player.ZeroVelocity();if (Input.GetKeyUp(KeyCode.Mouse1))stateMachine.ChangeState(player.idleState);Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);if (player.transform.position.x > mousePosition.x && player.facingDir == 1)player.Flip();else if(player.transform.position.x < mousePosition.x && player.facingDir == -1)player.Flip();}
}

PlayerCatchSwordState.cs

實現接劍狀態到空閑狀態的轉換。
實現接劍時根據劍的位置左右翻轉和后退效果。
設置處于忙碌中,防止滑動。

//PlayerCatchSwordState: 玩家接劍狀態
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayerCatchSwordState : PlayerState
{private Transform sword;public PlayerCatchSwordState(PlayerStateMachine _stateMachine, Player _player, string _animBoolName) : base(_stateMachine, _player, _animBoolName){}public override void Enter(){base.Enter();sword = player.sword.transform;if (player.transform.position.x > sword.position.x && player.facingDir == 1)player.Flip();else if (player.transform.position.x < sword.position.x && player.facingDir == -1)player.Flip();rb.velocity = new Vector2 (player.swordReturnImpact * -player.facingDir,rb.velocity.y);}public override void Exit(){base.Exit();player.StartCoroutine("BusyFor", 0.1f);}public override void Update(){base.Update();if (triggerCalled)stateMachine.ChangeState(player.idleState);}
}

PlayerGroundedState.cs

添加player是否被分配劍的判斷,根據判斷轉到瞄準狀態或回收劍。

//超級狀態PlayerGroundedState:接地狀態
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayerGroundedState : PlayerState
{//構造函數public PlayerGroundedState(PlayerStateMachine _stateMachine, Player _player, string _animBoolName) : base(_stateMachine, _player, _animBoolName){}//進入public override void Enter(){base.Enter();}//退出public override void Exit(){base.Exit();}//更新public override void Update(){base.Update();if (Input.GetKeyDown(KeyCode.Mouse1) && HasNoSword())stateMachine.ChangeState(player.aimSword);if (Input.GetKeyDown(KeyCode.Q))stateMachine.ChangeState(player.counterAttack);if (Input.GetKeyDown(KeyCode.Mouse0))stateMachine.ChangeState(player.primaryAttack);if(!player.isGroundDetected())stateMachine.ChangeState(player.airState);if (Input.GetKeyDown(KeyCode.Space)&& player.isGroundDetected())stateMachine.ChangeState(player.jumpState);}private bool HasNoSword(){if (!player.sword){return true;}player.sword.GetComponent<Sword_Skill_Controller>().ReturnSword();return false;}
}

PlayerAnimationTriggers.cs

實現在動畫播完觸發事件時扔出劍。

//PlayerAnimationTriggers:觸發器組件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayerAnimationTriggers : MonoBehaviour
{private Player player => GetComponentInParent<Player>();private void AnimationTrigger(){player.AnimationTrigger();}private void AttackTrigger(){Collider2D[] colliders = Physics2D.OverlapCircleAll(player.attackCheck.position, player.attackCheckRadius);foreach(var hit in colliders){if (hit.GetComponent<Enemy>() != null)hit.GetComponent<Enemy>().Damage();}}private void ThrowSword(){SkillManager.instance.sword.CreateSword();}
}

Sword_Skill.cs

創建Skill相關信息,和瞄準點所需信息。
創造劍的函數CreateSword,實例化劍并調用函數設置劍的相關變量,將劍分配給player,關閉瞄準點顯示。
實現瞄準點相關函數,包括計算瞄準方向、生成點瞄準點、激活/關閉瞄準點、計算運動軌跡。

//Sword_Skill:擲劍技能
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Sword_Skill : Skill
{[Header("Skill Info")][SerializeField] private GameObject swordPrefab;[SerializeField] private Vector2 launchForce;[SerializeField] private float swordGravity;[SerializeField] private float returnSpeed;[Header("Aim dots")][SerializeField] private int numberOfDots;[SerializeField] private float spaceBetweenDots;[SerializeField] private GameObject dotPrefab;[SerializeField] private Transform dotsParent;private GameObject[] dots;private Vector2 finalDir;protected override void Start(){base.Start();GenerateDots();}protected override void Update(){if(Input.GetKeyUp(KeyCode.Mouse1))finalDir = new Vector2(AimDirection().normalized.x * launchForce.x , AimDirection().normalized.y * launchForce.y);if(Input.GetKey(KeyCode.Mouse1)){for(int i = 0; i < dots.Length; i++){dots[i].transform.position = DotsPosition(i * spaceBetweenDots);}}}public void CreateSword(){GameObject newSword = Instantiate(swordPrefab, player.transform.position, transform.rotation);Sword_Skill_Controller newSwordScript = newSword.GetComponent<Sword_Skill_Controller>();newSwordScript.SetupSword(finalDir, swordGravity, player, returnSpeed);player.AssignNewSword(newSword);DotsActive(false);}public Vector2 AimDirection(){Vector2 playerPosition = player.transform.position;Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);Vector2 direction = mousePosition - playerPosition;return direction;}private void GenerateDots(){dots = new GameObject[numberOfDots];for (int i = 0; i < numberOfDots; i++){dots[i] = Instantiate(dotPrefab, player.transform.position, Quaternion.identity, dotsParent);dots[i].SetActive(false);}}public void DotsActive(bool _isActive){for (int i = 0; i < dots.Length; i++){dots[i].SetActive(_isActive);}}private Vector2 DotsPosition(float t){Vector2 position = (Vector2)player.transform.position +new Vector2(AimDirection().normalized.x * launchForce.x, AimDirection().normalized.y * launchForce.y) * t +0.5f * (Physics2D.gravity * swordGravity) * (t * t);return position;}
}

Sword_Skill_Controller.cs

獲取相關組件信息,創建相關變量并賦值。
根據Sword_Skill傳值,設置劍的相關參數,設置劍的動畫。
實現包圍盒觸發器函數,設置劍的相關參數,設置劍的動畫。
實現到接劍狀態的轉換,讓劍飛回玩家位置。實現劍返回的參數設置。

//Sword_Skill_Controller:擲劍技能控制器
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Sword_Skill_Controller : MonoBehaviour
{private Animator anim;private Rigidbody2D rb;private CircleCollider2D cd;private Player player;private bool canRotate = true;private bool isReturning;private float returnSpeed = 12;private void Awake(){anim = GetComponentInChildren<Animator>();rb = GetComponent<Rigidbody2D>();cd = GetComponent<CircleCollider2D>();}public void SetupSword(Vector2 _dir, float _gravityScale, Player _player, float _returnSpeed){player = _player;rb.velocity = _dir;rb.gravityScale = _gravityScale;returnSpeed = _returnSpeed;anim.SetBool("Rotation", true);}public void ReturnSword(){rb.constraints = RigidbodyConstraints2D.FreezeAll;transform .parent = null;isReturning = true;}private void Update(){if (canRotate){transform.right = rb.velocity;}if (isReturning){transform.position = Vector2.MoveTowards(transform.position, player.transform.position, returnSpeed * Time.deltaTime);if(Vector2.Distance(transform.position, player.transform.position) < 1)player.CatchTheSword();}}private void OnTriggerEnter2D(Collider2D collision){if (isReturning)return;StuckInto(collision);}private void StuckInto(Collider2D collision){anim.SetBool("Rotation", false);canRotate = false;cd.enabled = false;rb.isKinematic = true;rb.constraints = RigidbodyConstraints2D.FreezeAll;transform.parent = collision.transform;}}

SkillManager.cs

創建投劍技能。

//SkillManager:玩家管理器
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SkillManager : MonoBehaviour
{public static SkillManager instance;public Dash_Skill dash { get; private set; }public Clone_Skill clone { get; private set; }public Sword_Skill sword { get; private set; }private void Awake(){if (instance != null && instance != this){Destroy(this.gameObject);}else{instance = this;}}private void Start(){dash = GetComponent<Dash_Skill>();clone = GetComponent<Clone_Skill>();sword = GetComponent<Sword_Skill>();}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/81979.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/81979.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/81979.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Oracle01-入門

零、文章目錄 Oracle01-入門 1、Oracle簡介 &#xff08;1&#xff09;數據庫基礎 數據庫基礎請參考&#xff1a;https://blog.csdn.net/liyou123456789/article/details/131207068 &#xff08;2&#xff09;Oracle是什么 ORACLE 數據庫系統是美國 ORACLE 公司&#xff…

springboot集成mybatis-plus詳細使用

以下是 Spring Boot 集成 MyBatis-Plus 的詳細步驟&#xff1a; 創建 Spring Boot 項目 可使用 Spring Initializr 快速創建項目&#xff0c;添加相關依賴。 引入依賴 在項目 pom.xml 文件中添加以下依賴&#xff1a; <!-- MyBatis-Plus 啟動依賴 --> <dependenc…

【wsl】命令說明,wsl的虛擬機ubuntu十分好用

wsl官方說明地址 wsl虛擬機安裝簡單方便&#xff0c;十分推薦。 安裝 在上方的官網鏈接有安裝辦法和各種操作指南&#xff0c;在此不再贅述。 安裝wsl后從微軟商店搜索ubuntu點擊后就能直接安裝虛擬機鏡像&#xff0c;多快好省。 wsl命令 閱讀官網文檔時發現wsl十分強大 …

Open CASCADE學習|判斷一點與圓弧的位置關系

一、引言 在計算機輔助設計&#xff08;CAD&#xff09;、計算機圖形學以及機械制造等眾多領域中&#xff0c;經常需要處理幾何圖形之間的位置關系判斷問題。其中&#xff0c;判斷一個點與圓弧的位置關系是一個基礎且重要的任務。Open CASCADE 作為一個強大的開源幾何建模內核…

<論文>(字節跳動)使用大語言模型進行時間序列理解和推理

一、摘要 本文介紹2024年12月字節跳動牽頭發表的大模型論文《ChatTS: Aligning Time Series with LLMs via Synthetic Data for Enhanced Understanding and Reasoning》。論文提出了 ChatTS 模型&#xff0c;用合成數據提升對時間序列的理解和推理能力。作者在紐約出租車乘客數…

大數據應用開發和項目實戰-電商雙11美妝數據分析

數據初步了解 &#xff08;head出現&#xff0c;意味著只出現前5行&#xff0c;如果只出現后面幾行就是tail&#xff09; info shape describe 數據清洗 重復值處理 這個重復值是否去掉要看實際情況&#xff0c;比如說&#xff1a;昨天賣了5瓶七喜&#xff0c;今天賣了5瓶七…

Vi/Vim 編輯器詳細指南

Vi/Vim 編輯器詳細指南 簡介一、模式詳解1. 命令模式(Normal Mode)2. 插入模式(Insert Mode)3. 可視模式(Visual Mode)4. 命令行模式(Ex Mode)二、核心操作1. 保存與退出2. 導航與移動3. 編輯與文本操作4. 搜索與替換三、高級技巧1. 多文件與窗口操作2. 宏錄制3. 寄存器…

kotlin 01flow-StateFlow 完整教程

一 Android StateFlow 完整教程&#xff1a;從入門到實戰 StateFlow 是 Kotlin 協程庫中用于狀態管理的響應式流&#xff0c;特別適合在 Android 應用開發中管理 UI 狀態。本教程將帶全面了解 StateFlow 的使用方法。 1. StateFlow 基礎概念 1.1 什么是 StateFlow? StateF…

開發搭載OneNet平臺的物聯網數據收發APP的設計與實現

一、開發環境與工具準備 工具安裝 下載HBuilderX開發版(推薦使用開發版以避免插件兼容性問題)安裝Node.js和npm(用于依賴管理及打包)配置Android Studio(本地打包需集成離線SDK)項目初始化 創建uni-app項目,選擇“默認模板”或“空白模板”安裝必要的UI庫(如uView或Van…

HHsuite3 的 HHblits 和 HHsearch比較

HHblits 與 HHsearch 的核心區別及遠源同源檢測能力對比 一、核心功能與定位差異 特征HHblitsHHsearch核心目標快速迭代搜索,構建高質量多序列比對(MSA)和 Profile HMM,用于大規模序列聚類與初步同源篩選。高精度 Profile HMM-HMM 比對,用于深度同源檢測與結構 / 功能預測…

【從零開始學習RabbitMQ | 第二篇】生成交換機到MQ的可靠性保障

目錄 ?編輯前言 交換機 Direct交換機與Fanout交換機的差異 Topic交換機 Topic交換機相比Direct交換機的差異 生成我們的交換機&#xff0c;隊列&#xff0c;以及綁定關系 基于代碼去生成交換機和隊列 基于注解去聲明隊列和交換機 消息轉換器 消息隊列的高可靠性 發送…

LeetCode 熱題 100 22. 括號生成

LeetCode 熱題 100 | 22. 括號生成 大家好&#xff0c;今天我們來解決一道經典的算法題——括號生成。這道題在 LeetCode 上被標記為中等難度&#xff0c;要求生成所有可能的并且有效的括號組合。這是一道非常經典的回溯法題目&#xff0c;非常適合用來練習遞歸和回溯的技巧。…

TestStand API 簡介

TestStand API 簡介 在自動化測試領域&#xff0c;TestStand 憑借其靈活的架構和強大的功能&#xff0c;成為眾多開發者的首選工具。而 TestStand API&#xff08;Application Programming Interface&#xff0c;應用程序編程接口&#xff09;則是打開 TestStand 強大功能的 “…

如何修改 JAR 包中的源碼

如何修改 JAR 包中的源碼 前言一、準備工作二、將 JAR 當作 ZIP 打開并提取三、重寫 Java 類方法 A&#xff1a;直接替換已編譯的 .class方法 B&#xff1a;運行時類路徑優先加載 四、修改 MyBatis&#xff08;或其他&#xff09;XML 資源五、重新打包 JAR&#xff08;命令行&a…

存算一體架構下的新型AI加速范式:從Samsung HBM-PIM看近內存計算趨勢

引言&#xff1a;突破"內存墻"的物理革命 馮諾依曼架構的"存儲-計算分離"設計正面臨根本性挑戰——在GPT-4等萬億參數模型中&#xff0c;數據搬運能耗已達計算本身的200倍。存算一體&#xff08;Processing-In-Memory, PIM&#xff09;技術通過?在存儲介…

藍橋杯15屆國賽 合法密碼

問題描述 小藍正在開發自己的 OJ 網站。他要求網站用戶的密碼必須符合以下條件&#xff1a; 長度大于等于 8 個字符&#xff0c;小于等于 16 個字符。必須包含至少 1 個數字字符和至少 1 個符號字符。 例如 **lanqiao2024!、-*/0601、8((>w<))8** 都是合法的密碼。 而…

Jenkins忘記admin密碼后的恢復步驟

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、pandas是什么&#xff1f;二、使用步驟 1.引入庫2.讀入數據 總結 前言 提示&#xff1a;這里可以添加本文要記錄的大概內容&#xff1a; 時間較長沒有使用…

C++ - 仿 RabbitMQ 實現消息隊列(1)(環境搭建)

C - 仿 RabbitMQ 實現消息隊列&#xff08;1&#xff09;&#xff08;環境搭建&#xff09; 什么是消息隊列核心特點核心組件工作原理常見消息隊列實現應用場景優缺點 項目配置開發環境技術選型 更換軟件源安裝一些工具安裝epel 軟件源安裝 lrzsz 傳輸工具安裝git安裝 cmake安裝…

簡單面試提問

Nosql非關系型數據庫&#xff1a; Mongodb&#xff1a;開源、json形式儲存、c編寫 Redis&#xff1a;key-value形式儲存&#xff0c;儲存在內存&#xff0c;c編寫 關系型數據庫&#xff1a; sqlite;&#xff1a;輕量型、0配置、磁盤存儲、支持多種語言 mysql&#xff1a;開源…

油氣地震資料信號處理中的NMO(正常時差校正)

油氣地震資料信號處理中的NMO&#xff08;正常時差校正&#xff09;介紹與應用 NMO基本概念 **正常時差校正&#xff08;Normal Moveout Correction&#xff0c;NMO&#xff09;**是地震資料處理中的一項關鍵技術&#xff0c;主要用于消除由于炮檢距&#xff08;source-recei…