1.环境创建
资源导入
-Image.unitypackage (地板材质图片)
-FX.unitypackage (下雨和雨滴特效)使用Cube创建环境
使用FX中的特效给环境添加下雨的效果
2.摄像机的跟随
开发摄像机的跟随
- 使用Vertor3.Lerp插值计算
- 摄像机的视角,让摄像机观察这角色
- Quanternuin.LookRotation得到一个四元数,表示跟参数一样方向的旋转
- Quanternion.Slerp按照圆弧插值,这个插值计算更适用于,角度变换
using UnityEngine;
using System.Collections;
public class FollowPlayer : MonoBehaviour {
private Transform player;
public float speed = 2;
void Start() {
player = GameObject.Find(Tags.player).transform; //查找主角的Transform组件
}
void Update() {
Vector3 targetPos = player.position + new Vector3(0f,2f,-4f); //设定摄像机和主角之间的距离
transform.position =Vector3.Lerp(transform.position,targetPos,speed*Time.deltaTime);
Quaternion targetRotation = Quaternion.LookRotation(player.position-transform.position);
transform.rotation = Quaternion.Slerp(transform.rotation,targetRotation,speed*Time.deltaTime);
}
}
3使用CharacterController控制行走
- 使用Input.GetAxis(“Horizontal”) 和 “Vertical”得到垂直和水平方向的值
- 使用CharacterController.SimpleMove(Vector3)参数表示运动的方向和速度 单位可以认为是 m/s
using UnityEngine;
using System.Collections;
public class PlayerMove : MonoBehaviour {
private CharacterController cc;
public float speed = 40;
private Animator animator;
void Awake() {
cc = transform.GetComponent<CharacterController>();
animator = this.GetComponent<Animator>();
}
void Update () {
float h = Input.GetAxis("Horizontal"); //按A的时候返回的是1 按D的时候返回的是-1
float v = Input.GetAxis("Vertical"); //w返回1,s返回-1
if (Mathf.Abs(h) > 0.1f || Mathf.Abs(v) > 0.1f)
{ //加上判断主要是防止当没有按下时朝向LookAt为空
Vector3 targetDir = new Vector3(h, 0, v); //方向
transform.LookAt(targetDir + transform.position); //朝着一个点(主角的坐标点加上一个方向)以主角的坐标为基准朝向一个点
cc.SimpleMove(transform.forward * speed * Time.deltaTime);
}
}
}
4-添加虚拟杆控制行走
使用NGUI做虚拟杆
- 触摸位置的检测UICamera.lastTouchPosition
- 根据触摸位置的移动虚拟按钮
- 得到虚拟杆横向和垂直的值,控制角色的移动
using UnityEngine;
using System.Collections;
public class JoyStick : MonoBehaviour {
private bool isPress = false;
private Transform button; //虚拟摇杆的小圆圈
public static float h = 0;
public static float v = 0;
void Awake() {
button = transform.Find(Tags.button); //标签多的话可以单独设置一个Tags脚本存放,以免自己拼写错string
}
void OnPress(bool isPress) {
this.isPress = isPress;
if (isPress == false) {
button.localPosition = Vector2.zero; //抬起时让摇杆的小圆圈位置归零
h = 0; v = 0;
}
}
void Update () {
if (isPress) {
Vector2 touchPos = UICamera.lastTouchPosition;
touchPos -= new Vector2(100,100);
float distance = Vector2.Distance(Vector2.zero, touchPos);
if (distance > 75){
touchPos = touchPos.normalized * 75;
button.localPosition = touchPos;
}else {
button.localPosition = touchPos;
}
h = touchPos.x / 75;
v = touchPos.y / 75;
}
}
}
using UnityEngine;
using System.Collections;
public class PlayerMove : MonoBehaviour {
private CharacterController cc;
public float speed = 40;
private Animator animator;
void Awake() {
cc = transform.GetComponent<CharacterController>();
animator = this.GetComponent<Animator>();
}
void Update () {
float h = Input.GetAxis("Horizontal"); //按A的时候返回的是1 按D的时候返回的是-1
float v = Input.GetAxis("Vertical"); //w返回1,s返回-1
if (JoyStick.h != 0 || JoyStick.v != 0){
h = JoyStick.h;
v = JoyStick.v;
}
if (Mathf.Abs(h) > 0.1f || Mathf.Abs(v) > 0.1f)
{ //加上判断主要是防止当没有按下时朝向LookAt为空
Vector3 targetDir = new Vector3(h, 0, v); //方向
transform.LookAt(targetDir + transform.position); //朝着一个点(主角的坐标点加上一个方向)以主角的坐标为基准朝向一个点
cc.SimpleMove(transform.forward * speed * Time.deltaTime);
}
}
}
5-主角的状态机设计
- 使用Unity里面的Animation State Machine动画状态机
- 动画的属性设置,分为循环动画和一次性动画
- 主角的动画分类
- 休闲
- 行走
- 攻击 Attack1 Attack2 RangeAttack
- 被攻击
(对于walk和stand动画,需要多勾选LoopTime和LoopPose,剩下的只勾选三个BakeIntoPose【表示任何用上bake into pose的对应的属性(例如:root transform rotation),将会直接由角色或物件的自身transform的rotation来决定角色方向(如由script直接驱动角色transform rotation角度令角色转身,适合的动画是原地踏步),反之就是用animation 本身的信息来驱动角色transform rotation(适合动画是转身动画本身就带动角色转动,如转45度转身动画,90度转身动画等)于动画基于模型】)
using UnityEngine;
using System.Collections;
public class PlayerMove : MonoBehaviour {
private CharacterController cc;
public float speed = 40;
private Animator animator;
void Awake() {
cc = transform.GetComponent<CharacterController>();
animator = this.GetComponent<Animator>();
}
void Update () {
float h = Input.GetAxis("Horizontal"); //按A的时候返回的是1 按D的时候返回的是-1
float v = Input.GetAxis("Vertical"); //w返回1,s返回-1
if (JoyStick.h != 0 || JoyStick.v != 0){
h = JoyStick.h;
v = JoyStick.v;
}
if (Mathf.Abs(h) > 0.1f || Mathf.Abs(v) > 0.1f)
{ //加上判断主要是防止当没有按下时朝向LookAt为空
animator.SetBool("Walk", true); //行走动画
if (animator.GetCurrentAnimatorStateInfo(0).IsName("PlayerRun")) //加一层判断,确保动画与实际行走相吻合
{
Vector3 targetDir = new Vector3(h, 0, v); //方向
transform.LookAt(targetDir + transform.position); //朝着一个点(主角的坐标点加上一个方向)以主角的坐标为基准朝向一个点
cc.SimpleMove(transform.forward * speed * Time.deltaTime);
}
}
else {
animator.SetBool("Walk",false); //Idel动画
}
}
}
6-攻击的动画控制
- 设置虚拟的攻击按钮
- 通过代码添加按钮的点击事件监听
- EventDelegate normalClickEvent = new EventDelegate(this, “OnNormalAttackButtonClick”);
- GameObject.Find(“NormalAttack”).GetComponent().onClick.Add(normalClickEvent);
- 给状态机设置状态条件
- 修改Trigger animator.SetTrigger(“AttackB”);
- 修改Bool animator.SetBool(“Walk”, false);
在Player@PlayerAttackA动画文件添加两个事件,这两个事件必须要在脚本中实现监听,否则会运行错误public void AttackBEvent2()/public void AttackBEvent1()。 实现连招
using UnityEngine;
using System.Collections;
public class PlayerAnimationAttack : MonoBehaviour {
private bool isCanAttackB;
private Animator animator;
void Start () {
animator=this.GetComponent<Animator>();
EventDelegate NormalAttackEvent = new EventDelegate(this,"OnNormalAttackClick"); //这个脚本上 的--方法
GameObject.Find("Normal").GetComponent<UIButton>().onClick.Add(NormalAttackEvent); //也可以在面板上赋值
EventDelegate RangeAttackEvent = new EventDelegate(this,"OnRangeAttackClick");
GameObject.Find("Range").GetComponent<UIButton>().onClick.Add(RangeAttackEvent);
EventDelegate RedAttackEvent = new EventDelegate(this, "OnRedAttackClikc");
GameObject go = GameObject.Find("RedAttack");
go.GetComponent<UIButton>().onClick.Add(RedAttackEvent);
go.SetActive(false); //go.active=false api已经过时
}
// Update is called once per frame
void Update () {
}
public void AttackBEvent1() {
isCanAttackB = true;
}
public void AttackBEvent2() {
isCanAttackB = false;
}
public void OnNormalAttackClick() {
if (animator.GetCurrentAnimatorStateInfo(0).IsName("PlayerAttackA") && isCanAttackB)
{ //按下两次normal 且在attackA播放区间
animator.SetTrigger("AttackB");
}
else
{
animator.SetTrigger("AttackA");
}
}
public void OnRangeAttackClick() {
animator.SetTrigger("AttackRange");
}
public void OnRedAttackClikc() {
animator.SetTrigger("AttackA");
}
}