您的位置:首页 > 移动开发 > Unity3D

对金玺曾版《Unity3D手机游戏开发》第三章“第一人称射击游戏”修改,使支持僵尸连续攻击

2015-12-24 19:18 791 查看
我个人觉得这本书写的至少很和我口味,而且他的光盘资料也很详尽,比如,一个实例,不仅有一个完整的实现工程,还有一份供作练习的工程(该工程中没有要练习的部分,而资源啥的都有),让人感觉很好。

这本书下载电子版也很方便,比如:http://download.csdn.net/detail/u012337666/8402165。

我接下来具体说说自己对 第三章“第一人称射击游戏”中的改动。

先上改动前的源代码:

using UnityEngine;
using System.Collections;

[AddComponentMenu("Game/Enemy")]
public class Enemy : MonoBehaviour {

// Transform组件
Transform m_transform;
//CharacterController m_ch;

// 动画组件
Animator m_ani;

// 寻路组件
NavMeshAgent m_agent;

// 主角
Player m_player;

// 角色移动速度
float m_movSpeed = 0.5f;

// 角色旋转速度
float m_rotSpeed = 120;

//  计时器
float m_timer=2;

// 生命值
int m_life = 15;

// 成生点
protected EnemySpawn m_spawn;

// Use this for initialization
void Start () {

// 获取组件
m_transform = this.transform;
m_ani = this.GetComponent<Animator>();
m_agent = GetComponent<NavMeshAgent>();

// 获得主角
m_player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>();

}

// 初始化
public void Init(EnemySpawn spawn)
{
m_spawn = spawn;

m_spawn.m_enemyCount++;
}

// 当被销毁时
public void OnDeath()
{
//更新敌人数量
m_spawn.m_enemyCount--;

// 加100分
GameManager.Instance.SetScore(100);

// 销毁
Destroy(this.gameObject);
}

// Update is called once per frame
void Update () {

// 如果主角生命为0,什么也不做
if (m_player.m_life <= 0)
return;

// 获取当前动画状态
AnimatorStateInfo stateInfo = m_ani.GetCurrentAnimatorStateInfo(0);

// 如果处于待机状态
if (stateInfo.nameHash == Animator.StringToHash("Base Layer.idle") && !m_ani.IsInTransition(0))
{
m_ani.SetBool("idle", false);

// 待机一定时间
m_timer -= Time.deltaTime;
if (m_timer > 0)
return;

// 如果距离主角小于1.5米,进入攻击动画状态
if (Vector3.Distance(m_transform.position, m_player. m_transform.position) < 1.5f)
{
m_ani.SetBool("attack", true);
}
else
{
// 重置定时器
m_timer=1;

// 设置寻路目标点
m_agent.SetDestination(m_player. m_transform.position);

// 进入跑步动画状态
m_ani.SetBool("run", true);
}
}

// 如果处于跑步状态
if (stateInfo.nameHash == Animator.StringToHash("Base Layer.run") && !m_ani.IsInTransition(0))
{

m_ani.SetBool("run", false);

// 每隔1秒重新定位主角的位置
m_timer -= Time.deltaTime;
if (m_timer < 0)
{
m_agent.SetDestination(m_player. m_transform.position);

m_timer = 1;
}

// 追向主角
MoveTo();

// 如果距离主角小于1.5米,向主角攻击
if (Vector3.Distance(m_transform.position, m_player. m_transform.position) <= 1.5f)
{
//停止寻路
m_agent.ResetPath();
// 进入攻击状态
m_ani.SetBool("attack", true);
}
}

// 如果处于攻击状态
if (stateInfo.nameHash == Animator.StringToHash("Base Layer.attack") && !m_ani.IsInTransition(0))
{

// 面向主角
RotateTo();

m_ani.SetBool("attack", false);

// 如果攻击动画播完,重新进入待机状态
if (stateInfo.normalizedTime >= 1.0f)
{
m_ani.SetBool("idle", true);

// 重置计时器
m_timer = 2;

m_player.OnDamage(1);
}
}

// 死亡
if (stateInfo.nameHash == Animator.StringToHash("Base Layer.death") && !m_ani.IsInTransition(0))
{
if (stateInfo.normalizedTime >= 1.0f)
{
OnDeath();

}
}

}

// 转向目标点
void RotateTo()
{
// 当前角度
Vector3 oldangle = m_transform.eulerAngles;

//  获得面向主角的角度
m_transform.LookAt(m_player.m_transform);
float target = m_transform.eulerAngles.y;

// 转向主角
float speed = m_rotSpeed * Time.deltaTime;
float angle = Mathf.MoveTowardsAngle(oldangle.y, target, speed);
m_transform.eulerAngles = new Vector3(0, angle, 0);
}

// 寻路移动
void MoveTo()
{
float speed = m_movSpeed * Time.deltaTime;
m_agent.Move(m_transform.TransformDirection((new Vector3(0, 0, speed))));

}

// 伤害
public void OnDamage(int damage)
{
m_life -= damage;

// 如果生命为0,销毁自身
if (m_life <= 0)
{
m_ani.SetBool("death", true);
}
}
}


我发现以书中的逻辑:僵尸每次到达攻击范围后,只会发起一次攻击,然后就转为IDEL状态,然后再检测是否有攻击,没有则继续追有则再发起攻击。

感觉运行起来的效果不是太好,因为僵尸只攻击一次(也就是挥一次手臂),然后就恢复站立状态,等下再挥,很不像僵尸的作风。至少也得连续挥舞吧。

连续挥舞有两种实现方式:

1.设置Attack的动画为Loop,然后将上述代码中的这一句if(stateInfo.normalizedTime > 1.0f),将1.0f稍微该大些,这样僵尸就会挥动很多次手臂。

但我觉得这种方式直接改掉了Attack这个动画本来的意义,不采用

2.对Animator中的Attack状态做些变化,目前Attack这个状态有下面的3个状态变化,

那么目前我就需要 一个attack -> attack的状态变化,以实现attack的连续执行。

设置如下:

这样就建立了attack->attack的动画过渡效果,然后还需要代码控制这一状态,于是再设置:

这样,当bool值attack的值为False时,Attack这个动画就不再重复了。

我改动的代码如下:

if (stateInfo.fullPathHash == Animator.StringToHash ("Base Layer.attack")
&& !m_ani.IsInTransition(0)) {
RotateTo ();
if (m_timer > 0) {
m_timer -= Time.deltaTime;
return;
}
m_ani.SetBool ("attack", false);
if (stateInfo.normalizedTime >= 1.0f) {
m_ani.SetBool ("idle", true);
m_timer = 0.1f;
}
}
这里,我将在RotateTo后面等待m_Timer的时间,这个时间也就是动画重复播放的时间,这个时间过之后,我就会设置Attack的值为False,然后当再播放完一次时,就设置idle为true,然后程序就会转而执行idle的部分了。

总体效果就是:僵尸到达攻击范围,攻击N次,然后停止,再继续攻击N次,直到主角死亡,或者不再攻击范围而追击主角。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: