实现鼠标点击的交互
public class PlayerInput : MonoBehaviour
{
public float horizontalInput;
public float verticalInput;
public bool mouseDown;
文章来源地址https://www.toymoban.com/news/detail-839632.html
// Update is called once per frame
void Update()
{ ///在游戏运行且鼠标当前未被按下的情况下,检测鼠标左键是否被用户按下,并记录这一交互状态。
if(!mouseDown&&Time.timeScale!=0 //游戏运行中,Time.timeScale等于0时,游戏时间会停止更新,此时不应该响应玩家输入
){
mouseDown=Input.GetMouseButtonDown(0);
}
horizontalInput=Input.GetAxis("Horizontal");
verticalInput=Input.GetAxis("Vertical");
//检测水平输入和垂直输入
}
/// <summary>
/// 当角色死亡(禁用)是调用OnDisable方法,关闭输入
/// </summary>
private void OnDisable() {
mouseDown=false;//清除鼠标输入
horizontalInput=0;
verticalInput=0;
}
}
状态机的初步设计
private void StateSwitchTo(CharacterState newState)
{
if(isPlayer){
playerInput.mouseDown=false;//清除鼠标输入,因为Enemy是由ai控制的,所以不需要mouseDown
}
//对于当前状态的处理
switch(currentState){
case CharacterState.Normal:
break;
case CharacterState.Attacking:
animator.SetTrigger("Attack");
break;
}
//对于新状态的处理
switch(newState){
case CharacterState.Normal:
break;
case CharacterState.Attacking:
animator.SetTrigger("Attack");
if(isPlayer)
attackStartTime=Time.time;//记录角色攻击开始时间的时间点
if(!isPlayer){
Quaternion newRotition=Quaternion.LookRotation(playerTarget.transform.position-transform.position);//计算角色的朝向
transform.rotation=newRotition;
}
break;
}
currentState=newState;
Debug.Log("state changed to "+currentState);
}
normal与attacking的状态转换
private void CalculateMove(){
///判断玩家是否在地面上并同时判断是否按下鼠标左键将状态切换到攻击状态,返回不执行移动逻辑
if(playerInput.mouseDown&&cc.isGrounded){
StateSwitchTo(CharacterState.Attacking);
return;
}
void FixedUpdate(){
///根据游戏角色的状态(正常或攻击)以及是否是玩家控制的角色来决定执行不同的移动计算逻辑
switch(currentState){
case CharacterState.Normal:
if(isPlayer)
CalculateMove();
else
CalculateEnemyMove();
break;
case CharacterState.Attacking:
if(isPlayer){
moveVelocity=Vector3.zero;//将移动速度归零,防止前一帧速度对滑动的干扰
if(Time.time<attackStartTime+slideDuration)//判断当前时间是否处于攻击开始时间与滑动持续时间之和的区间
{
float passedTime=Time.time-attackStartTime;
float lerpTime=passedTime/slideDuration; // 计算出一个介于0和1之间的浮点数lerpTime,它代表了当前时间相对于整个动画周期的位置,。通过将这个lerpTime与起始和结束状态进行插值运算,可以得到每个时间点应有的状态值,从而实现平滑过渡效果。
moveVelocity=Vector3.Lerp(transform.forward*slideSpeed,Vector3.zero,lerpTime);
//Vector3.Lerp(...): 这是Unity中用于执行三维空间向量线性插值的方法。其三个参数分别为起始向量、结束向量和时间系数(通常介于0到1之间)。
//函数执行后,moveVelocity将被赋予从起始向量平滑过渡到结束向量的结果。在给定的lerpTime时间内,物体的滑动速度会逐渐减小,最终变为零。这可以用来实现诸如减速停止、渐隐消失等效果。
}
}
break;
}
攻击状态的设计与实现:
玩家攻击
玩家攻击动画的实现:
点击图示位置打开动画控制器
添加一个Trigger类型的Attack,右键-Create Sub-State Machine,如图连接各个状态,转换条件取消exit time,condition设置为attack
双击attack状态机,如图
创建combo 01状态,连接exit状态,combo 01参数如下图所示
combo 01->exit参数如图设置
保留exit time:确保动画完整播放
Transition Duration值为0:防止动画播放交叉
编辑attack动画,如图添加一个AttackAnimationEnds,实现攻击结束后回到之前的状态。
代码:
private void StateSwitchTo(CharacterState newState)
{
if(isPlayer){
playerInput.mouseDown=false;//清除鼠标输入,因为Enemy是由ai控制的,所以不需要mouseDown
}
//对于当前状态的处理
switch(currentState){
case CharacterState.Normal:
break;
case CharacterState.Attacking:
animator.SetTrigger("Attack");
break;
}
//对于新状态的处理
switch(newState){
case CharacterState.Normal:
break;
case CharacterState.Attacking:
animator.SetTrigger(" Attack");
if(isPlayer)
attackStartTime=Time.time;//记录角色攻击开始时间的时间点
if(!isPlayer){
Quaternion newRotition=Quaternion.LookRotation(playerTarget.transform.position-transform.position);//计算角色的朝向
transform.rotation=newRotition;
}
break;
}
currentState=newState;
Debug.Log("state changed to "+currentState);
}
public void AttackAnimationEnds(){
StateSwitchTo(CharacterState.Normal);
}//攻击状态转换为常态
玩家攻击特效的实现
预制体位置如图,将该预制体作为player-Vfx的子对象,在检查器相应位置引入
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.VFX;//引入VFX
public class PlayerVFXManager : MonoBehaviour
{
public VisualEffect visualEffect;//定义变量visualEffect
public ParticleSystem particleSystem;
public void UpdateStep(bool state){
if(state){
visualEffect.Play();//播放VFX动画
}
else{
visualEffect.Stop();
}
}
public void PlayBlade01(){
particleSystem.Play();
}
}
敌人攻击
敌人攻击动画的实现
打开敌人的动画控制器后将敌人的攻击动作设计完毕,后续与玩家的设计原理相同
private void CalculateEnemyMove(){
if(Vector3.Distance(transform.position,playerTarget.position)>agent.stoppingDistance){
agent.SetDestination(playerTarget.position);
animator.SetFloat("Speed",0.2f);
}else{
agent.SetDestination(transform.position);
animator.SetFloat("Speed",0f);
StateSwitchTo(CharacterState.Attacking);//靠近玩家时切换为攻击状态
}
敌人攻击特效的实现
位置如图,
参数设置如图,实现方法与player相同(不同点:删掉visual effect中的initial event name)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.VFX;
public class EnemyVFXManager : MonoBehaviour
{
public VisualEffect visualEffect;
public VisualEffect AttackVFX;
public void PlayerAttackAnimation(){
AttackVFX.Play();
}
public void BurstFootStep(){
visualEffect.Play();
}
}
出现了一点小问题,敌人并没有朝向玩家攻击,解决方法
case CharacterState.Attacking:
if(!isPlayer){
Quaternion newRotition=Quaternion.LookRotation(playerTarget.transform.position-transform.position);//计算角色的朝向
transform.rotation=newRotition;
}
break;
}
实现攻击时滑动的功能
private float attackStartTime;
public float slideDuration=0.5f;
public float slideSpeed=0.4f;
switch(newState){
case CharacterState.Normal:
break;
case CharacterState.Attacking:
animator.SetTrigger("Attack");
if(isPlayer)
attackStartTime=Time.time;//记录角色攻击开始时间的时间点
moveVelocity=Vector3.zero;//将移动速度归零,防止前一帧速度对滑动的干扰
if(Time.time<attackStartTime+slideDuration)//判断当前时间是否处于攻击开始时间与滑动持续时间之和的区间
{
float passedTime=Time.time-attackStartTime;
float lerpTime=passedTime/slideDuration; // 计算出一个介于0和1之间的浮点数lerpTime,它代表了当前时间相对于整个动画周期的位置,。通过将这个lerpTime与起始和结束状态进行插值运算,可以得到每个时间点应有的状态值,从而实现平滑过渡效果。
moveVelocity=Vector3.Lerp(transform.forward*slideSpeed,Vector3.zero,lerpTime);
//Vector3.Lerp(...): 这是Unity中用于执行三维空间向量线性插值的方法。其三个参数分别为起始向量、结束向量和时间系数(通常介于0到1之间)。
//函数执行后,moveVelocity将被赋予从起始向量平滑过渡到结束向量的结果。在给定的lerpTime时间内,物体的滑动速度会逐渐减小,最终变为零。这可以用来实现诸如减速停止、渐隐消失等效果。
} 文章来源:https://www.toymoban.com/news/detail-839632.html
到了这里,关于Unity 3D RPG游戏的设计与实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!