Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考
此代码仅为较上一P有所改变的代码
【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili
AudioManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class AudioManager : MonoBehaviour
{public static AudioManager instance;[SerializeField] private float sfxMinimumDistance;[SerializeField] private AudioSource[] sfx;[SerializeField] private AudioSource[] bgm;public bool playBgm;private int bgmIndex;private void Awake(){if (instance != null){Destroy(instance.gameObject);}elseinstance = this;}private void Update(){if (!playBgm)StopAllBGM();else{if (!bgm[bgmIndex].isPlaying)PlayBGM(bgmIndex);}}public void PlaySFX(int _sfxIndex,Transform _source){//if (sfx[_sfxIndex].isPlaying)//防止出现多个相同物体同时发出声音//{// return;//}if (_source != null && Vector2.Distance(PlayerManager.instance.player.transform.position, _source.position) > sfxMinimumDistance)//防止东西很远的情况下发出声音return;if(_sfxIndex < sfx.Length){sfx[_sfxIndex].pitch = Random.Range(.85f, 1.1f);sfx[_sfxIndex].Play();}}public void StopSFX(int _sfxIndex){sfx[_sfxIndex].Stop();}public void PlayRandomBGM(){bgmIndex = Random.Range(0, bgm.Length);PlayBGM(bgmIndex);}public void PlayBGM(int _bgmIndex){bgmIndex = _bgmIndex;StopAllBGM();if (_bgmIndex < sfx.Length){bgm[_bgmIndex].Play();}}public void StopAllBGM(){for(int i = 0; i < bgm.Length; i++){bgm[i].Stop();}}
}
Checkpoint.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Checkpoint : MonoBehaviour
{private Animator anim;public string id;public bool activationStatus;private void Awake(){anim = GetComponent<Animator>();}private void Start(){anim = GetComponent<Animator>();}[ContextMenu("Generate checkpoint id")]private void GenerateId()//制作ID函数{id = System.Guid.NewGuid().ToString();//不根据path,直接生成id}private void OnTriggerEnter2D(Collider2D collision){if(collision.GetComponent<Player>()!=null){ActivateCheckpoint();}}public void ActivateCheckpoint()//激活检查点函数{if (activationStatus == false) AudioManager.instance.PlaySFX(5, transform);anim.SetBool("active", true);activationStatus = true;}
}
ItemObject.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ItemObject : MonoBehaviour
{private SpriteRenderer sr;[SerializeField] private Rigidbody2D rb;//设置速度[SerializeField] private ItemData ItemData;[SerializeField] private Vector2 velocity;//设置速度private void SetupVisuals(){if (ItemData == null)return;GetComponent<SpriteRenderer>().sprite = ItemData.icon;gameObject.name = ItemData.name;}public void SetupItem(ItemData _itemData,Vector2 _velocity)设置实例函数{ItemData = _itemData;rb.velocity = _velocity;//设置速度SetupVisuals();}public void PickupItem()//拾取函数打包{if(!Inventory.instance.CanAddItem()&&ItemData.itemType == ItemType.Equipment)//修复在Inventory满时捡钱装备并销毁它的bug{rb.velocity = new Vector2(0, 7);return;}AudioManager.instance.PlaySFX(18, transform);Inventory.instance.AddItem(ItemData);Destroy(gameObject);}
}
Skill.cs
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;public class Skill : MonoBehaviour
{public float cooldown;public float cooldownTimer;protected Player player;//拿到playerprotected virtual void Start(){player = PlayerManager.instance.player;//拿到playerCheckUnlock();}protected virtual void Update(){cooldownTimer -= Time.deltaTime;}protected virtual void CheckUnlock()//再次打开游戏时读取数据文件后使技能可以使用的函数{}public virtual bool CanUseSkill(){if (cooldownTimer < 0){UseSkill();cooldownTimer = cooldown;return true;}else{Debug.Log("Skill is on cooldown");return false;}}public virtual void UseSkill(){// do some skill thing}//整理能返回最近敌人位置的函数protected virtual Transform FindClosestEnemy(Transform _checkTransform){Collider2D[] colliders = Physics2D.OverlapCircleAll(_checkTransform.position, 25);//找到环绕自己的所有碰撞器float closestDistance = Mathf.Infinity;//正无穷大的表示形式(只读)Transform closestEnemy = null;//https://docs.unity3d.com/cn/current/ScriptReference/Mathf.Infinity.htmlforeach (var hit in colliders){if (hit.GetComponent<Enemy>() != null){float distanceToEnemy = Vector2.Distance(_checkTransform.position, hit.transform.position);//拿到与敌人之间的距离if (distanceToEnemy < closestDistance)//比较距离,如果离得更近,保存这个敌人的位置,更改最近距离{closestDistance = distanceToEnemy;closestEnemy = hit.transform;}}}return closestEnemy;}}
Blackhole_Skill.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class Blackhole_Skill : Skill
{[SerializeField] private UI_SkillTreeSlot balckholeUnlockButton;public bool blackholeUnlocked { get; private set; }[SerializeField]private float maxSize;//最大尺寸[SerializeField] private float growSpeed;//变大速度[SerializeField] private float shrinkSpeed;//缩小速度[SerializeField] private GameObject blackholePrefab;[Space][SerializeField] private float blackholeDuration;[SerializeField] int amountOfAttacks = 4;[SerializeField] float cloneAttackCooldown = .3f;Blackhole_Skill_Controller currentBlackhole;private void UnlockBlackhole(){if(balckholeUnlockButton.unlocked){blackholeUnlocked = true;}}public override bool CanUseSkill(){return base.CanUseSkill();}public override void UseSkill(){base.UseSkill();GameObject newBlackhole = Instantiate(blackholePrefab,player.transform.position,Quaternion.identity);currentBlackhole = newBlackhole.GetComponent<Blackhole_Skill_Controller>();currentBlackhole.SetupBlackhole(maxSize,growSpeed,shrinkSpeed,amountOfAttacks,cloneAttackCooldown,blackholeDuration);AudioManager.instance.PlaySFX(3, player.transform);AudioManager.instance.PlaySFX(6, player.transform);}protected override void Start(){base.Start();balckholeUnlockButton.GetComponent<Button>().onClick.AddListener(UnlockBlackhole);}protected override void Update(){base.Update();}public bool SkillCompleted(){if(currentBlackhole == null)return false;if (currentBlackhole.playerCanExitState){return true;}else{return false;}}//把随机敌人半径改成黑洞半径的一半就行public float GetBlackholeRadius(){return maxSize / 2;}protected override void CheckUnlock(){base.CheckUnlock();UnlockBlackhole();}
}
PlayerGroundState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//GroundState用于保证只有在Idle和Move这两个地面状态下才能调用某些函数,并且稍微减少一点代码量
public class PlayerGroundState : PlayerState
{public PlayerGroundState(Player _player, PlayerStateMachine _stateMachine, string _animBoolName) : base(_player, _stateMachine, _animBoolName){}public override void Enter(){base.Enter();}public override void Exit(){base.Exit();}public override void Update(){base.Update();if(Input.GetKeyDown(KeyCode.R)&& player.skill.blackhole.blackholeUnlocked){if (player.skill.blackhole.cooldown > 0)return;stateMachine.ChangeState(player.blackhole);}if(Input.GetKeyDown(KeyCode.Mouse1)&&HasNoSword() && player.skill.sword.swordUnlocked)//点击右键进入瞄准状态,当sword存在时,不能进入aim状态{stateMachine.ChangeState(player.aimSword);}if(Input.GetKeyDown(KeyCode.Q) && player.skill.parry.parryUnlocked)//摁Q进入反击状态{stateMachine.ChangeState(player.counterAttack);}if(Input.GetKeyDown(KeyCode.Mouse0))//p38 2.从ground进入攻击状态{stateMachine.ChangeState(player.primaryAttack);}if(player.IsGroundDetected()==false){stateMachine.ChangeState(player.airState);}// 写这个是为了防止在空中直接切换为moveState了。if (Input.GetKeyDown(KeyCode.Space) && player.IsGroundDetected()){stateMachine.ChangeState(player.jumpState);}//空格切换为跳跃状态}private bool HasNoSword()//用这个函数同时控制了是否能进入aimSword和如果sword存在便使他回归player的功能{if(!player.sword){return true;}player.sword.GetComponent<Sword_Skill_Controller>().ReturnSword();return false;}
}
PlayerMoveState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayerMoveState : PlayerGroundState
{public PlayerMoveState(Player _player, PlayerStateMachine _stateMachine, string _animBoolName) : base(_player, _stateMachine, _animBoolName){}//构造函数,用于传递信息。//当外补New出对象时,New出的对象里传入参数public override void Enter(){base.Enter();AudioManager.instance.PlaySFX(14,null);}public override void Exit(){base.Exit();AudioManager.instance.StopSFX(14);}public override void Update(){base.Update();player.SetVelocity(xInput*player.moveSpeed, rb.velocity.y);//player.rb.newVelocity.y默认的为0,player.moveSpeed在player中定义,但可以在Unity引擎中更改if (xInput==0|| player.IsWallDetected()){stateMachine.ChangeState(player.idleState);//在这里我们能使用Player里的东西,主要是因为我们通过构造函数传过来Player实体,如果不传,则无法使用已经存在了的Player实体。}}
}