【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili
教程源地址:https://www.udemy.com/course/2d-rpg-alexdev/
对一些实现的代码进行一些清理工作
PlayerFX.cs
using Cinemachine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//2024.12.6
public class PlayerFX : EntityFX
{
[Header("震动特效")]//Screen shake FX
[SerializeField] private float shakeMultiplier;
private CinemachineImpulseSource screenShake;
public Vector3 shakeSwordImpact;//投掷震动效果向量
public Vector3 shakeHighDamage;//高伤害震动效果向量
[Header("残影特效")]//After image FX
[SerializeField] private float afterImageCooldown;
[SerializeField] private GameObject afterImagePrefab;
[SerializeField] private float colorLooseRate;//颜色丢失率
private float afterImageCooldownTimer;
[Space]
[SerializeField] private ParticleSystem dustFx;
protected override void Start()
{
base.Start();
screenShake = GetComponent<CinemachineImpulseSource>();
}
private void Update()
{
afterImageCooldownTimer -= Time.deltaTime;
}
public void CreateAfterImage()//生成残影
{
if (afterImageCooldownTimer < 0)
{
afterImageCooldownTimer = afterImageCooldown;//重置冷却时间
GameObject newAfterImage = Instantiate(afterImagePrefab, transform.position + new Vector3(0, .25f, 0), transform.rotation);//生成残影实例
newAfterImage.GetComponent<AfterImageFX>().SetupAfterImage(colorLooseRate, sr.sprite);
}
}
public void ScreenShake(Vector3 _shakePower)
{
screenShake.m_DefaultVelocity = new Vector3(_shakePower.x * player.facingDir, _shakePower.y) * shakeMultiplier;
screenShake.GenerateImpulse();
}
public void PlayDustFX()
{
if (dustFx != null)
dustFx.Play();
}
}
EntityFX.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Cinemachine;
using TMPro;
public class EntityFX : MonoBehaviour
{
protected Player player;
protected SpriteRenderer sr;
[Header("弹出文本")]//Pop Up Text
[SerializeField] private GameObject popUpTextPrefab;
[Header("闪光特效")]//Flash FX
[SerializeField] private float flashDuration;
[SerializeField] private Material hitMat;
private Material originalMat;
[Header("异常状态颜色")]//Ailment colors
[SerializeField] private Color[] chillColor;
[SerializeField] private Color[] igniteColor;
[SerializeField] private Color[] shockColor;
[Header("异常状态粒子")]//Ailment particles
[SerializeField] private ParticleSystem igniteFx;
[SerializeField] private ParticleSystem chillFx;
[SerializeField] private ParticleSystem shockFx;
[Header("攻击特效")]//Hit FX
[SerializeField] private GameObject hitFX;
[SerializeField] private GameObject criticalHitFx;
protected virtual void Start()
{
sr = GetComponentInChildren<SpriteRenderer>();
player = PlayerManager.instance.player;
originalMat = sr.material;
}
public void CreatePopUpText(string _text)//生成弹出文本
{
float randomx = Random.Range(-0.5f, 0.5f);
float randomy = Random.Range(1.5f, 3);
Vector3 positionOffset = new Vector3(randomx, randomy, 0);
GameObject newText = Instantiate(popUpTextPrefab, transform.position + positionOffset, Quaternion.identity);
newText.GetComponent<TextMeshPro>().text = _text;
}
public void MakeTransprent(bool _transparent)//攻击命中时候的透明特效
{
if (_transparent)
sr.color = Color.clear;
else
sr.color = Color.white;
}
private IEnumerator FlashFX()//定义了一个私有的协程 FlashFX,用于在一段时间内改变 SpriteRenderer 的材质,然后恢复原始材质。
{
sr.material = hitMat;
Color currentColor = sr.color;//保存最开始的颜色,闪光之后变回去
sr.color = Color.white;
yield return new WaitForSeconds(flashDuration);
sr.color = currentColor;
sr.material = originalMat;
}
//P59,骷髅战士的血条闪烁
private void RedColorBlink()
{
if (sr.color != Color.white)
sr.color = Color.white;
else
{
sr.color = Color.red;
}
}
private void CancelColorChange()
{
CancelInvoke();//调用 CancelInvoke 方法取消所有与当前游戏对象关联的 Invoke 调用。
sr.color = Color.white;
igniteFx.Stop();
chillFx.Stop();
shockFx.Stop();
}
//10月30日
//三个状态的携程
public void ShockFxFor(float _seconds)
{
shockFx.Play();
InvokeRepeating("ShockColorFx", 0, .3f);
Invoke("CancelColorChange", _seconds);
}
public void IgniteFxFor(float _seconds)
{
igniteFx.Play();
InvokeRepeating("IgniteColorFx", 0, .3f);
Invoke("CancelColorChange", _seconds);
}
public void ChillFxFor(float _seconds)
{
chillFx.Play();
InvokeRepeating("ChillColorFx", 0, .3f);
Invoke("CancelColorChange", _seconds);
}
private void ShockColorFx()
{
if (sr.color != shockColor[0])
sr.color = shockColor[0];
else
{
sr.color = shockColor[1];
}
}
private void IgniteColorFx()//燃烧状态颜色
{
if (sr.color != igniteColor[0])
sr.color = igniteColor[0];
else
{
sr.color = igniteColor[1];
}
}
private void ChillColorFx()//燃烧状态颜色
{
if (sr.color != chillColor[0])
sr.color = chillColor[0];
else
{
sr.color = chillColor[1];
}
}
//2024.12.5
public void CreateHitFX(Transform _target, bool _critical)//攻击特效
{
float zRotation = Random.Range(-90, 90);
float xPosition = Random.Range(-.5f, .5f);
float yPosition = Random.Range(-.5f, .5f);
Vector3 hitFXRotation = new Vector3(0, 0, zRotation);//生成的旋转向量
GameObject hitPrefab = hitFX;
if (_critical)//如果暴击
{
hitPrefab = criticalHitFx;
float yRotation = 0;
zRotation = Random.Range(-45, 45);
if (GetComponent<Entity>().facingDir == -1)//如果玩家朝向左边
yRotation = 180;
hitFXRotation = new Vector3(0, yRotation, zRotation);
}
GameObject newHitFX = Instantiate(hitPrefab, _target.position + new Vector3(xPosition, yPosition), Quaternion.identity);//,_target);
newHitFX.transform.Rotate(hitFXRotation);//设置旋转角度
Destroy(newHitFX, .3f);
}
}
Player.cs
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
//9月24日p33翻转,明天记得复习状态机
public class Player : Entity
{
[Header("攻击参数")]//Attack details
public Vector2[] attackMovement;//攻击时的移动速度
public float counterAttackDuration = .2f;//反击持续时间
public bool isBusy { get; private set; }//解决攻击一瞬间进入idle和move状态的问题
[Header("移动参数")]//Move info
public float moveSpeed = 12f;
public float jumpForce ;
public float swordReturnImpact;
private float defaultMoveSpeed;
private float defaultJumpForce;
[Header("冲刺参数")]//Dash info
//[SerializeField] private float dashCoolDown; P64任务管理器里面做了冲刺技能的冷却
//private float dashUsageTimer;
public float dashSpeed;
public float dashDuration;
private float defaultDashSpeed;
public float dashDir { get; private set; }
public SkillManager skill { get; private set; }
public GameObject sword { get; private set; }
public PlayerFX fx { get; private set; }
#region States
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 wallSlide{ get; private set; }
public PlayerWallJumpState wallJump { 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; }
public PlayerBlackHoleState blackHole { get; private set; }
public PlayerDeathState deadState { get; private set; }
#endregion
protected override void Awake()
{
base.Awake();
stateMachine = new PlayerStateMachine();
idleState = new PlayerIdleState(this, stateMachine, "Idle");
moveState = new PlayerMoveState(this, stateMachine, "Move");
jumpState = new PlayerJumpState(this, stateMachine, "Jump");
airState = new PlayerAirState(this, stateMachine, "Jump");
dashState = new PlayerDashState(this, stateMachine, "Dash");
wallSlide= new PlayerWallSlideState(this, stateMachine, "WallSlide");
wallJump = new PlayerWallJumpState(this, stateMachine, "Jump");
primaryAttack = new PlayerPrimaryAttackState(this, stateMachine, "Attack");
counterAttack = new PlayerCounterAttackState(this, stateMachine, "CounterAttack");
aimSword = new PlayerAimSwordState(this, stateMachine, "AimSword");
catchSword = new PlayerCatchSwordState(this, stateMachine, "CatchSword");
blackHole = new PlayerBlackHoleState(this, stateMachine, "Jump");
deadState = new PlayerDeathState(this, stateMachine, "Die");
}
protected override void Start()
{
base.Start();//继承entity中的start
fx = GetComponent<PlayerFX>();//获取玩家特效
skill = SkillManager.instance;//在各种技能中使用player.skill来调用技能
stateMachine.Initialize(idleState);
defaultMoveSpeed = moveSpeed;
defaultJumpForce = jumpForce;
defaultDashSpeed = dashSpeed;
}
protected override void Update()
{
if (Time.timeScale == 0)
return;
base.Update();
stateMachine.currentState.Update();
CheckForDashInput();
if(Input.GetKeyDown(KeyCode.F) && skill.crystal.crystalUnlocked)
skill.crystal.CanUseSkill();//肯定要改到其他地方
if (Input.GetKeyDown(KeyCode.Alpha1))
Inventory.instance.UseFlask();
}
public override void SlowEntityBy(float _slowPercentage, float _slowDuration)
{
moveSpeed =moveSpeed * (1 - _slowPercentage);
jumpForce = jumpForce * (1 - _slowPercentage);
dashSpeed = dashSpeed * (1 - _slowPercentage);
anim.speed = anim.speed * (1 - _slowPercentage);
Invoke("ReturnDefaultSpeed", _slowDuration);
}
protected override void ReturnDefaultSpeed()
{
base.ReturnDefaultSpeed();
moveSpeed = defaultMoveSpeed;
jumpForce = defaultJumpForce;
dashSpeed = defaultDashSpeed;
}
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();
private void CheckForDashInput()
{
if(IsWallDetected() )
return;
if(skill.dash.dashUnlocked == false)
return;
if (Input.GetKeyDown(KeyCode.LeftShift) && SkillManager.instance.dash.CanUseSkill())
{
dashDir = Input.GetAxisRaw("Horizontal");
if (dashDir == 0)
{
dashDir = facingDir;
}
stateMachine.ChangeState(dashState);
}
}
public override void Die()
{
base.Die();
stateMachine.ChangeState(deadState);
}
protected override void SetupZeroKnockbackPower()
{
knockbackPower = new Vector2(0, 0);
}
}
Enemy.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : Entity
{
[SerializeField]protected LayerMask whatIsPlayer;// LayerMask: 这是字段的类型,LayerMask 是一个用于指定物理层的结构体 ; [SerializeField] 属性使得 whatIsPlayer 字段在 Unity 编辑器中可见
[Header("眩晕参数")]//Stunned info
public float stunDuration;
public Vector2 stunDirection;
public bool canBeStunned;
[SerializeField] protected GameObject counterImage;
[Header("移动参数")]//Move info
public float moveSpeed ;
public float idleTime;
public float battleTime;
private float defaultMoveSpeed;
[Header("攻击参数")]//Attack info
public float attackDistance;
public float attackCoolDown;
public float minAttackCoolDown;//最小攻击冷却时间
public float maxAttackCoolDown;
[HideInInspector] public float lastTimeAttack;
public EnemyStateMachine stateMachine { get; private set; }
public EntityFX fx { get; private set; }
private Player player;
public string lastAnimBoolName { get; protected set; }// 存储上一个动画布尔值的名称
protected override void Start()
{
base.Start();
fx = GetComponent<EntityFX>();//使用EntityFX来控制玩家的特效
}
protected override void Awake()
{
base.Awake();
stateMachine = new EnemyStateMachine();
defaultMoveSpeed = moveSpeed;
}
protected override void Update()
{
base.Update();
stateMachine.currentState.Update();
//Debug.Log(IsPlayerDetected().collider.gameObject.name+"i see it");
}
public virtual void AssignLastBoolName(string _animBoolName) => lastAnimBoolName = _animBoolName;
public override void SlowEntityBy(float _slowPercentage, float _slowDuration)
{
moveSpeed = moveSpeed * (1 - _slowPercentage);
anim.speed = anim.speed * (1 - _slowPercentage);
Invoke("ReturnDefaultSpeed", _slowDuration);
}
protected override void ReturnDefaultSpeed()
{
base.ReturnDefaultSpeed();
moveSpeed = defaultMoveSpeed;
}
public virtual void FreezeTime(bool _timeFrozen)
{
if (_timeFrozen)
{
moveSpeed = 0;
anim.speed = 0;
}
else
{
moveSpeed = defaultMoveSpeed;
anim.speed = 1;
}
}
public virtual void FreezeTimeFor(float _duration) => StartCoroutine(FreezeTimerCoroutine(_duration));
protected virtual IEnumerator FreezeTimerCoroutine(float _seconds)//IEnumerator 是一个接口,用于支持迭代器的实现。迭代器允许你在集合上进行迭代操作。Unity 中的协程(Coroutine)也使用 IEnumerator 来实现异步操作。
{
FreezeTime(true);
yield return new WaitForSeconds(_seconds);//等待 _seconds 秒
FreezeTime(false);
}
#region Counter Attack Window
public virtual void OpenCounterAttackWindow()// virtual 方法可以在派生类中使用 override 关键字进行重写,以提供不同的实现。
{
canBeStunned = true;
counterImage.SetActive(true);//这行代码将 counterImage 游戏对象设置为激活状态,使其在游戏中可见。
}
public virtual void CloseCounterAttackWindow()
{
canBeStunned = false;
counterImage.SetActive(false);
}
#endregion
public virtual bool CanBeStunned()
{
if (canBeStunned)
{
CloseCounterAttackWindow();
return true;
}
return false;
}
public virtual void AnimationFinishTrigger() => stateMachine.currentState.AnimationFinishTrigger();
public virtual RaycastHit2D IsPlayerDetected() => Physics2D.Raycast(wallCheck.position, Vector2.right * facingDir, wallCheckDistance, whatIsPlayer);
protected override void OnDrawGizmos()
{
base.OnDrawGizmos();
Gizmos.color = Color.yellow;
Gizmos.DrawLine(transform.position, new Vector3(transform.position.x + attackDistance * facingDir, transform.position.y));
}
}
Entity.cs
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class Entity : MonoBehaviour
{
#region Components
//实体相关的组件
public SpriteRenderer sr { get; private set; }
public Animator anim { get; private set; }//{ get; private set; }: 这是属性的访问器,表示该属性可以被外部读取,但只能在类内部设置。
public Rigidbody2D rb { get; private set; }
public CharacterStats stats { get; private set; }//统计数据的组件
public CapsuleCollider2D cd { get; private set; }
#endregion
[Header("Knockback info")]
[SerializeField] protected Vector2 knockbackPower;//击退方向
[SerializeField] protected float knockbackDuration;//击退持续时间
protected bool isKnocked;
[Header("Collision info")]
public Transform attackCheck;//攻击检测点
public float attackCheckRadius;//攻击检测半径
[SerializeField] protected Transform groundCheck;//Transform变量用于存储地面检测的位置...........作用: protected 修饰符使得类成员只能在包含它的类及其派生类中访问。
[SerializeField] protected float groundCheckDistance;
[SerializeField] protected Transform wallCheck;
[SerializeField] protected float wallCheckDistance;
[SerializeField] protected LayerMask whatIsGround;//LayerMask 是一个用于指定物理层的结构体,常用于物理操作中以过滤特定层的碰撞体
public int knockbackDir { get; private set; }
public int facingDir { get; private set; } = 1;//facingDir变量用于存储玩家的朝向,1表示向右,-1表示向左
protected bool facingRight = true;
public System.Action onFlipped;
protected virtual void Awake()
{
}
protected virtual void Start()
{
sr= GetComponentInChildren<SpriteRenderer>();//使用SpriteRenderer来控制玩家的显示
anim = GetComponentInChildren<Animator>();//使用Animator来控制玩家的动画
rb = GetComponent<Rigidbody2D>();//使用Rigidbody2D来控制玩家的移动
stats = GetComponent<CharacterStats>();
cd = GetComponent<CapsuleCollider2D>();
}
protected virtual void Update()
{
}
public virtual void SlowEntityBy(float _slowPercentage, float _slowDuration)
{
}
protected virtual void ReturnDefaultSpeed()
{
anim.speed = 1;
}
//2024年10月30日,给技能加上伤害
public virtual void DamageImpact() => StartCoroutine("HitKnockback"); //受到攻击的特效
public virtual void SetupKnockbackDir(Transform _damageDirection)
{
if (_damageDirection.position.x > transform.position.x)
knockbackDir = -1; // 攻击者在右侧,被攻击者向左击退
else if (_damageDirection.position.x < transform.position.x)
knockbackDir = 1; // 攻击者在左侧,被攻击者向右击退
}
public void SetupKnockbackPower(Vector2 _knockbackPower) =>knockbackPower = _knockbackPower;//击退力
protected virtual IEnumerator HitKnockback()//IEnumerator 是一个接口,用于支持迭代器的实现。迭代器允许你在集合上进行迭代操作。Unity 中的协程(Coroutine)也使用 IEnumerator 来实现异步操作。
{
isKnocked = true;
rb.velocity = new Vector2(knockbackPower.x * knockbackDir, knockbackPower.y);// 设置击退的速度
yield return new WaitForSeconds(knockbackDuration);// 等待击退持续时间
isKnocked = false;// 恢复击退状态
SetupZeroKnockbackPower();// 恢复击退力
}
protected virtual void SetupZeroKnockbackPower()//受到高伤害时候被击倒
{
}
#region Velocity
public void SetZeroVelocity()
{
if (isKnocked)
return;
rb.velocity = new Vector2(0, 0);
}
public void SetVelocity(float _xVelocity, float _yVelocity)
{
if (isKnocked)
return;//如果玩家正在被击退,那么就不执行下面的代码
rb.velocity = new Vector2(_xVelocity, _yVelocity);
FlipController(_xVelocity);
}
#endregion
#region Collision
public virtual bool IsGroundDetected() => Physics2D.Raycast(groundCheck.position, Vector2.down, groundCheckDistance, whatIsGround);//bool类型数值取true和false
public virtual bool IsWallDetected() => Physics2D.Raycast(wallCheck.position, Vector2.right * facingDir, wallCheckDistance, whatIsGround);//virtual: 这个修饰符表示该方法可以在派生类中被重写。
protected virtual void OnDrawGizmos()
{
Gizmos.DrawLine(groundCheck.position, new Vector3(groundCheck.position.x, groundCheck.position.y - groundCheckDistance));
Gizmos.DrawLine(wallCheck.position, new Vector3(wallCheck.position.x + wallCheckDistance, wallCheck.position.y));
Gizmos.DrawWireSphere(attackCheck.position, attackCheckRadius);
}
#endregion
#region Flip
public virtual void Flip()
{
facingDir = facingDir * -1;
facingRight = !facingRight;
transform.Rotate(0, 180, 0);
if(onFlipped != null)//没有添加上的角色就不会报错
onFlipped();
}
public virtual void FlipController(float _x)
{
if (_x > 0 && !facingRight)
Flip();
else if (_x < 0 && facingRight)
Flip();
}
#endregion
public virtual void Die()
{
}
}
标签:void,float,P174,up,private,恶魔城,protected,new,public
From: https://blog.csdn.net/suzh1qian/article/details/144292855