您的位置:首页 > 文旅 > 美景 > 免费制作网页_无症状感染者会自愈吗_网站排名优化技巧_关于友情链接的作用有

免费制作网页_无症状感染者会自愈吗_网站排名优化技巧_关于友情链接的作用有

2024/12/23 11:09:52 来源:https://blog.csdn.net/cyr___/article/details/142317803  浏览:    关键词:免费制作网页_无症状感染者会自愈吗_网站排名优化技巧_关于友情链接的作用有
免费制作网页_无症状感染者会自愈吗_网站排名优化技巧_关于友情链接的作用有

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开发2D类银河恶魔城游戏学习笔记目录


文章目录

  • Unity开发2D类银河恶魔城游戏学习笔记
  • 前言
  • 一、概述
  • 二、创建骷髅攻击动画
  • 三、骷髅攻击的实现
    • (1)整理代码
    • (2)创建SkeletonAttackState
    • (3)触发器的实现
    • (4)攻击的实现
  • 四、骷髅攻击的冷却
  • 总结 完整代码
    • EnemyState.cs
    • Enemy.cs
    • Enemy_SkeletonAnimationTriggers.cs
    • Enemy_Skeleton.cs
    • SkeletonBattleState.cs
    • SkeletonAttackState.cs
    • SkeletonMoveState.cs


前言

本文为Udemy课程The Ultimate Guide to Creating an RPG Game in Unity学习笔记,如有错误,欢迎指正。

本节实现敌人的攻击状态。

Udemy课程地址

对应视频:
Enemy’s Attack State


一、概述

在骷髅进入战斗状态并走到距离玩家很近的距离时,就要进入攻击状态攻击玩家。本节中我们就实现骷髅的攻击状态。
这一部分状态转换条件较多,如下图:
在这里插入图片描述
在这里插入图片描述

二、创建骷髅攻击动画

我们创建动画skeletonAttack
层次面板中选中Enemy_skeleton下的Animator,在Animation面板中创建动画
将精灵表SkeletonAttack内容全部拖入,采样率改为15
动画创建的更详细讲解见Unity教程(零)Unity和VS的使用相关内容
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

连接状态机,并添加过渡条件Attack,并修改过渡设置
添加bool型条件变量Attack,并连接过渡
在这里插入图片描述
Entry->skeletonAttack的过渡,加条件变量
在这里插入图片描述

skeletonAttack->Exit的过渡,加条件变量,并更改设置
在这里插入图片描述

三、骷髅攻击的实现

(1)整理代码

上一节我们用到了很多次骷髅的速度,所以我们在敌人状态中创建刚体,使代码更简洁。

protected Rigidbody2D rb;public virtual void Enter(){rb = enemyBase.rb;triggerCalled = false;enemyBase.anim.SetBool(animBoolName, true);}

SkeletonBattleState状态Update函数中改为

    public override void Update(){base.Update();if (enemy.IsPlayerDetected()){if (enemy.IsPlayerDetected().distance < enemy.attackDistance){stateMachine.ChangeState(enemy.attackState);}}if(player.position.x > enemy.transform.position.x)moveDir = 1;else if(player.position.x < enemy.transform.position.x)moveDir = -1;enemy.SetVelocity(enemy.moveSpeed * moveDir, rb.velocity.y);}

SkeletonMoveState状态Update函数中改为

    public override void Update(){base.Update();enemy.SetVelocity(enemy.moveSpeed*enemy.facingDir,rb.velocity.y);if(!enemy.isGroundDetected() || enemy.isWallDetected()){enemy.Flip();stateMachine.ChangeState(enemy.idleState);}}

(2)创建SkeletonAttackState

首先创建SkeletonAttackState,它继承自EnemyState,通过菜单生成构造函数和重写。
在这里插入图片描述
添加Enemy_Skeleton变量,并修改构造函数中传入值

    protected Enemy_Skeleton enemy;public SkeletonAttackState(EnemyStateMachine _stateMachine, Enemy _enemyBase, Enemy_Skeleton _enemy, string _animBoolName) : base(_stateMachine, _enemyBase, _animBoolName){enemy = _enemy;}

(3)触发器的实现

和Player一样,敌人攻击的结束也要借助触发器来实现。
详细讲解请见Unity教程(八)角色基本攻击的实现
我们在状态基类EnemyState中添加改变参数的函数,若触发器被调用,则triggerCalled置为真。添加代码:

    //修改触发器参数public virtual void AnimationFinishTrigger(){triggerCalled = true;}

在Enemy中进行调用

    //设置触发器public virtual void AnimationTrigger() => stateMachine.currentState.AnimationFinishTrigger();

我们创建骷髅的触发器脚本Enemy_SkeletonAnimationTriggers。获取Animator的父组件Enemy_Skeleton,并调用其中的AnimationTrigger()

//Enemy_SkeletonAnimationTriggers:触发器组件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Enemy_SkeletonAnimationTriggers : MonoBehaviour
{private Enemy_Skeleton enemy => GetComponentInParent<Enemy_Skeleton>();private void AnimationTrigger(){enemy.AnimationTrigger();}
}

把触发器脚本挂在Enemy_Skeleton的Animator下面 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/41a27165de72476f966005e4d1ccdc39.png#pic_center) 我们在skeletonAttack的最后一帧设置事件帧,调用触发器函数设置triggerCalled参数。

先把白色竖线拉到最后一帧,点击动画面板左边钉子符号的标志添加事件。
在这里插入图片描述
在这里插入图片描述
点击选中事件帧,在右边面板选中函数AnimationTrigger
Fuction->Enemy_SkeletonAnimationTriggers->Methods->AnimationTrigger()
在这里插入图片描述

(4)攻击的实现

我们要在Enemy_Skeleton中创建骷髅攻击状态。

public SkeletonAttackState attackState { get; private set; }protected override void Awake()
{base.Awake();idleState = new SkeletonIdleState(stateMachine,this,this,"Idle");moveState = new SkeletonMoveState(stateMachine, this,this, "Move");battleState = new SkeletonBattleState(stateMachine, this, this, "Move");attackState = new SkeletonAttackState(stateMachine, this, this, "Attack");
}

在SkeletonBattleState中添加状态转换,当距离过近时转换到攻击状态。

    public override void Update(){base.Update();if (enemy.IsPlayerDetected()){if (enemy.IsPlayerDetected().distance < enemy.attackDistance){stateMachine.ChangeState(enemy.attackState);}}if(player.position.x > enemy.transform.position.x)moveDir = 1;else if(player.position.x < enemy.transform.position.x)moveDir = -1;enemy.SetVelocity(enemy.moveSpeed * moveDir, enemy.rb.velocity.y);}

我们在SkeletonAttackState中实现攻击的具体内容。
首先我们希望骷髅靠近玩家时,先停止移动再攻击,所以我们先把骷髅速度赋零。
在攻击动画播放完触发器被触发时,攻击结束,转换到战斗状态。
在这里插入图片描述
点击Animator也能看到左侧Attack变量在转换(切换很快,不仔细看不清楚)。

四、骷髅攻击的冷却

现在骷髅的攻击是连续不断的,在游戏中通常小怪是按照一定的频率进行的,让玩家有一定的躲避时间。
我们给骷髅小怪加上攻击冷却时间,实现上可以参照Player的连击。
我们在Enemy中添加冷却时间attackCoolDown,记录上次攻击的时间lastTimeAttacked用于实现。

    [Header("Attack Info")]public float attackDistance;public float attackCoolDown;[HideInInspector] public float lastTimeAttacked;

在SkeletonAttackState中,每次退出攻击状态时,更新lastTimeAttacked。

    public override void Exit(){base.Exit();enemy.lastTimeAttacked=Time.time;}

在SkeletonBattleState中添加CanAttack函数,利用现在的时间与上次攻击时间加冷却时间比较,判断冷却时间是否已经过去,骷髅是否处于可攻击状态。
先判断是否能检测到玩家,再判断玩家与骷髅的距离,再做攻击冷却的判断,条件都成立时转到战斗状态。

public override void Update(){base.Update();if (enemy.IsPlayerDetected()){if (enemy.IsPlayerDetected().distance < enemy.attackDistance){if(CanAttack())stateMachine.ChangeState(enemy.attackState);}}if(player.position.x > enemy.transform.position.x)moveDir = 1;else if(player.position.x < enemy.transform.position.x)moveDir = -1;enemy.SetVelocity(enemy.moveSpeed * moveDir, rb.velocity.y);}private bool CanAttack(){if (Time.time >= enemy.lastTimeAttacked + enemy.attackCoolDown)return true;return false;}

设置合适的冷却时间
在这里插入图片描述
效果如下
在这里插入图片描述
我们可以看到骷髅每隔一段时间进行攻击了。但是有个很明显的问题,在攻击的间隙骷髅不会停止移动,玩家会被推着走。这个问题下节我们再来解决。

总结 完整代码

EnemyState.cs

增加刚体和触发函数调用

//EnemyState:敌人状态基类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class EnemyState
{protected EnemyStateMachine stateMachine;protected Enemy enemyBase;protected Rigidbody2D rb;private string animBoolName;protected float stateTimer;protected bool triggerCalled;//构造函数public EnemyState(EnemyStateMachine _stateMachine, Enemy _enemyBase, string _animBoolName){this.stateMachine = _stateMachine;this.enemyBase = _enemyBase;this.animBoolName = _animBoolName;}public virtual void Enter(){rb = enemyBase.rb;triggerCalled = false;enemyBase.anim.SetBool(animBoolName, true);}public virtual void Update(){stateTimer-= Time.deltaTime;}public virtual void Exit(){enemyBase.anim.SetBool(animBoolName, false);}//修改触发器参数public virtual void AnimationFinishTrigger(){triggerCalled = true;}
}

Enemy.cs

添加攻击相关变量,添加触发函数调用。

//Enemy:敌人基类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Enemy : Entity
{[SerializeField] protected LayerMask WhatIsPlayer;[Header("Move Info")]public float moveSpeed = 1.5f;public float idleTime = 2.0f;[Header("Attack Info")]public float attackDistance;public float attackCoolDown;[HideInInspector] public float lastTimeAttacked;public EnemyStateMachine stateMachine;protected override void Awake(){base.Awake();stateMachine = new EnemyStateMachine();}protected override void Update(){base.Update();stateMachine.currentState.Update();}//设置触发器public virtual void AnimationTrigger() => stateMachine.currentState.AnimationFinishTrigger();public virtual RaycastHit2D IsPlayerDetected()=>Physics2D.Raycast(transform.position, Vector2.right * facingDir, 50 ,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));}
}

Enemy_SkeletonAnimationTriggers.cs

触发器组件

//Enemy_SkeletonAnimationTriggers:触发器组件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Enemy_SkeletonAnimationTriggers : MonoBehaviour
{private Enemy_Skeleton enemy => GetComponentInParent<Enemy_Skeleton>();private void AnimationTrigger(){enemy.AnimationTrigger();}
}

Enemy_Skeleton.cs

增加攻击状态创建

//Enemy_Skeleton:骷髅敌人
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Enemy_Skeleton : Enemy
{#region 状态public SkeletonIdleState idleState { get; private set; }public SkeletonMoveState moveState { get; private set; }public SkeletonBattleState battleState { get; private set; }public SkeletonAttackState attackState { get; private set; }#endregionprotected override void Awake(){base.Awake();idleState = new SkeletonIdleState(stateMachine,this,this,"Idle");moveState = new SkeletonMoveState(stateMachine, this,this, "Move");battleState = new SkeletonBattleState(stateMachine, this, this, "Move");attackState = new SkeletonAttackState(stateMachine, this, this, "Attack");}protected override void Start(){base.Start();stateMachine.Initialize(idleState);}protected override void Update(){base.Update();}}

SkeletonBattleState.cs

添加攻击冷却判断条件,修改转到攻击状态的条件,修改刚体速度部分代码

//SkeletonBattleState:骷髅战斗状态
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SkeletonBattleState : EnemyState
{private Transform player;private Enemy_Skeleton enemy;private int moveDir;public SkeletonBattleState(EnemyStateMachine _stateMachine, Enemy _enemyBase, Enemy_Skeleton _enemy, string _animBoolName) : base(_stateMachine, _enemyBase, _animBoolName){enemy=_enemy;}public override void Enter(){base.Enter();player = GameObject.Find("Player").transform;}public override void Exit(){base.Exit();}public override void Update(){base.Update();if (enemy.IsPlayerDetected()){if (enemy.IsPlayerDetected().distance < enemy.attackDistance){if(CanAttack())stateMachine.ChangeState(enemy.attackState);}}if(player.position.x > enemy.transform.position.x)moveDir = 1;else if(player.position.x < enemy.transform.position.x)moveDir = -1;enemy.SetVelocity(enemy.moveSpeed * moveDir, rb.velocity.y);}private bool CanAttack(){if (Time.time >= enemy.lastTimeAttacked + enemy.attackCoolDown)return true;return false;}
}

SkeletonAttackState.cs

创建攻击状态,攻击时速度置零,触发器触发转回到战斗状态

//SkeletonAttackState:骷髅攻击状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SkeletonAttackState : EnemyState
{protected Enemy_Skeleton enemy;public SkeletonAttackState(EnemyStateMachine _stateMachine, Enemy _enemyBase, Enemy_Skeleton _enemy, string _animBoolName) : base(_stateMachine, _enemyBase, _animBoolName){enemy = _enemy;}public override void Enter(){base.Enter();}public override void Exit(){base.Exit();enemy.lastTimeAttacked=Time.time;}public override void Update(){base.Update();enemy.ZeroVelocity();if (triggerCalled)stateMachine.ChangeState(enemy.battleState);}
}

SkeletonMoveState.cs

修改刚体速度部分代码

//SkeletonMoveState:骷髅移动状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SkeletonMoveState : SkeletonGroundedState
{public SkeletonMoveState(EnemyStateMachine _stateMachine, Enemy _enemyBase, Enemy_Skeleton enemy, string _animBoolName) : base(_stateMachine, _enemyBase, enemy, _animBoolName){}public override void Enter(){base.Enter();}public override void Exit(){base.Exit();}public override void Update(){base.Update();enemy.SetVelocity(enemy.moveSpeed*enemy.facingDir,rb.velocity.y);if(!enemy.isGroundDetected() || enemy.isWallDetected()){enemy.Flip();stateMachine.ChangeState(enemy.idleState);}}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com