赞
踩
踩死人物时会有烟雾上升
首先下载资源https://pan.baidu.com/s/1QhfsEbu7RD22rQhZAx6ysA
然后导入到unity中,搭建好场景(导入env和天空盒),添加人物,把人物放到空物体下,给人物添加角色控制器,设置好人物的胶囊碰撞体积
角色控制器带有部分的刚体功能,并且自带胶囊碰撞器
接下来创建脚本,由于需要玩家和敌人,我们可以让玩家和敌人共同继承自一个character基类。
character.cs:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Character : MonoBehaviour { protected CharacterController cc; protected Animator animator; public float runSpeed; public float jumpPower=10; Vector3 pendingVelocity;//包含了速度和方向 // Start is called before the first frame update void Awake() { cc = GetComponent<CharacterController>(); animator = GetComponentInChildren<Animator>(); } // Update is called once per frame void Update() { //移动 pendingVelocity.z = 0f; cc.Move(pendingVelocity * Time.deltaTime); //模拟重力 pendingVelocity.y += cc.isGrounded ? 0f : Physics.gravity.y * 10f * Time.deltaTime; } public void Jump() { if (cc.isGrounded) { pendingVelocity.y = jumpPower; } } public void Move(float inputX) { pendingVelocity.x = inputX * runSpeed; } public void Rotate(Vector3 lookDir,float turnSpeed) { } public void Death() { } public void TakeDamage(Character inflicter,int damage)//承受伤害 //inflicter是加害者,受害者是自己 { } public void AttackCheck()//攻击检测 { } public void GrabCheck() { } }
playercharacter.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCharacter : Character
{
// Update is called once per frame
}
playercontroller.cs:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { PlayerCharacter character; // Start is called before the first frame update void Start() { character = GetComponent<PlayerCharacter>(); } // Update is called once per frame void Update() { var h = Input.GetAxis("Horizontal"); character.Move(h); if (h != 0) { var dir = Vector3.right * h; character.Rotate(dir, 10); } if (Input.GetKeyDown(KeyCode.Space)) { character.Jump(); } if (Input.GetKeyDown(KeyCode.G)) { character.GrabCheck(); } } }
以上脚本写完后 就可以实现移动和跳跃的效果了
这里需要注意一点:charactercontroller自带Move函数,参数是vec3向量,效果是提供速度,但并不会提供重力,因此想要实现重力的效果需要自己模拟:
private float gravity=10f;
//模拟重力
pendingVelocity.y -= cc.isGrounded ? 0f : gravity * 10f * Time.deltaTime;
跳跃的动画效果:
当人物不处于地面时则播放跳跃的动画,当人物处于地面时,结束跳跃的动画进入地面的动画,因此在move和jump之间添加bool变量:grounded
move由blend tree组合而成:
需要注意 speed参数是自己添加的参数,范围为0到20,这里的20是设置好的最大速度:
动画状态间的切换:
然后还需要在update函数中更新speed和grounded的值即可:
void Update()
{
//移动
pendingVelocity.z = 0f;
cc.Move(pendingVelocity * Time.deltaTime);
animator.SetFloat("Speed", cc.velocity.magnitude);
animator.SetBool("Grounded", cc.isGrounded);
//更新重力
pendingVelocity.y -= cc.isGrounded ? 0f : gravity * 10f * Time.deltaTime;
}
跑步:
跳跃:
旋转需要在两个角度间进行球面插值
public void Rotate(Vector3 lookDir,float turnSpeed) { rotateComplete = false; var targetPos = transform.position + lookDir; var characterPos = transform.position; //去除y轴影响 characterPos.y = 0; targetPos.y = 0; //角色面朝目标的向量 Vector3 faceToDir = targetPos - characterPos; //角色面朝目标方向的四元数 Quaternion faceToQuat = Quaternion.LookRotation(faceToDir); //球面插值 Quaternion slerp = Quaternion.Slerp(transform.rotation, faceToQuat, turnSpeed * Time.deltaTime); if (slerp == faceToQuat) { rotateComplete = true; } transform.rotation = slerp; }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerCamera : MonoBehaviour { public Transform target; public float distance = 8.0f; public float height=-1.0f; // Start is called before the first frame update // Update is called once per frame void LateUpdate() { if (!target) return; transform.position = target.position; transform.position -= Vector3.forward * distance; transform.position = new Vector3(transform.position.x, height, transform.position.z); } }
直接将这个脚本添加给相机,然后给相机添加上目标即可
public int damage = 100;
public int health = 100;
public GameObject deathFX;
void Update() { …… AttackCheck(); } public void TakeDamage(Character inflicter,int damage)//承受伤害 //inflicter是加害者,受害者是自己 { inflicter.Jump(); health -= damage; if (health <= 0) { Death(); } } public void AttackCheck()//攻击检测 { var dist = cc.height / 2; RaycastHit hit; if(Physics.Raycast(transform.position,Vector3.down,out hit, dist + 0.05f)) { if (hit.transform.GetComponent<Character>() && hit.transform != transform) { hit.transform.GetComponent<Character>().TakeDamage(this, damage); } } }
创建一个材质(在project窗口中),设置属性
然后再创建一个粒子系统(在和人物那些所在的窗口中)
设置好一系列的属性:
在人物死亡时,产生一个粒子:
public void Death()
{
//fx:特效
var fx = Instantiate(deathFX, transform.position, Quaternion.LookRotation(new Vector3(0,1,0)));
//Destroy(fx, 2);
Destroy(gameObject);
}
由于抓取只在player中,因此在playercharacter中实现grab函数:
PlayerCharacter.cs:
public Transform grabSocket; public Transform grabObject; public void GrabCheck() { if (grabObject != null && rotateComplete) { grabObject.transform.SetParent(null); grabObject.GetComponent<Rigidbody>().isKinematic = false; grabObject = null; } else { var dist = cc.radius + 1f; RaycastHit hit; if(Physics.Raycast(transform.position,transform.forward,out hit, dist)) { if (hit.collider.CompareTag("GrabBox")) { grabObject = hit.transform; grabObject.SetParent(grabSocket); grabObject.localPosition = Vector3.zero; grabObject.localRotation = Quaternion.identity; grabObject.GetComponent<Rigidbody>().isKinematic = true; } } } }
抓取物体的动画需要单独在手上实现,因此我们可以新开一个动画层来实现
然后我们需要将基类中的update函数类型设置为protected型,然后在子类playerCharacter中修改动画控制器中的bool值,以及别忘了修改另外一个动画层的layer值:
private void Start()
{
animator.SetLayerWeight(armsAnimatorLayer, 1f);//除了基层以外,其他的层次初始权重默认是0,这里需要手动设置。
}
private void Update()
{
animator.SetBool("Grab", grabObject ? true : false);
base.Update();
}
另外,我们还可以点击这里
在这里可以查看动画,以及每个动画的遮罩信息:
Character.cs:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Character : MonoBehaviour { protected CharacterController cc; protected Animator animator; public float runSpeed=20; public float jumpPower=20; public int damage = 100; public int health = 100; public GameObject deathFX; private float gravity = 10f; Vector3 pendingVelocity;//包含了速度和方向 protected bool rotateComplete = true; // Start is called before the first frame update void Awake() { cc = GetComponent<CharacterController>(); animator = GetComponentInChildren<Animator>(); } // Update is called once per frame protected void Update() { //移动 pendingVelocity.z = 0f; cc.Move(pendingVelocity * Time.deltaTime); animator.SetFloat("Speed", cc.velocity.magnitude); animator.SetBool("Grounded", cc.isGrounded); //更新重力 pendingVelocity.y -= cc.isGrounded ? 0f : gravity * 10f * Time.deltaTime; AttackCheck(); } public void Jump() { if (cc.isGrounded) { pendingVelocity.y = jumpPower; } } public void Move(float inputX) { pendingVelocity.x = inputX * runSpeed; } public void Rotate(Vector3 lookDir,float turnSpeed) { rotateComplete = false; var targetPos = transform.position + lookDir; var characterPos = transform.position; //去除y轴影响 characterPos.y = 0; targetPos.y = 0; //角色面朝目标的向量 Vector3 faceToDir = targetPos - characterPos; //角色面朝目标方向的四元数 Quaternion faceToQuat = Quaternion.LookRotation(faceToDir); //球面插值 Quaternion slerp = Quaternion.Slerp(transform.rotation, faceToQuat, turnSpeed * Time.deltaTime); if (slerp == faceToQuat) { rotateComplete = true; } transform.rotation = slerp; } public void Death() { //fx:特效 var fx = Instantiate(deathFX, transform.position, Quaternion.LookRotation(new Vector3(0,1,0))); //Destroy(fx, 2); Destroy(gameObject); } public void TakeDamage(Character inflicter,int damage)//承受伤害 //inflicter是加害者,受害者是自己 { inflicter.Jump(); health -= damage; if (health <= 0) { Death(); } } public void AttackCheck()//攻击检测 { var dist = cc.height / 2; RaycastHit hit; if(Physics.Raycast(transform.position,Vector3.down,out hit, dist + 0.05f)) { if (hit.transform.GetComponent<Character>() && hit.transform != transform) { hit.transform.GetComponent<Character>().TakeDamage(this, damage); } } } public void GrabCheck() { } }
PlayerController.cs:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { PlayerCharacter character; // Start is called before the first frame update void Start() { character = GetComponent<PlayerCharacter>(); } // Update is called once per frame void Update() { var h = Input.GetAxis("Horizontal"); character.Move(h); if (h != 0) { var dir = Vector3.right * h; character.Rotate(dir, 10); } if (Input.GetKeyDown(KeyCode.Space)) { character.Jump(); } if (Input.GetKeyDown(KeyCode.G)) { character.GrabCheck(); } } }
PlayerCharacter:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerCharacter : Character { public Transform grabSocket; public Transform grabObject; public int armsAnimatorLayer = 1; private void Start() { animator.SetLayerWeight(armsAnimatorLayer, 1f);//除了基层以外,其他的层次初始权重默认是0,这里需要手动设置。 } private void Update() { animator.SetBool("Grab", grabObject ? true : false); base.Update(); } // Update is called once per frame public void GrabCheck() { if (grabObject != null && rotateComplete) { grabObject.transform.SetParent(null); grabObject.GetComponent<Rigidbody>().isKinematic = false; grabObject = null; } else { var dist = cc.radius + 1f; RaycastHit hit; if(Physics.Raycast(new Vector3(transform.position.x, transform.position.y+0.5f, transform.position.z), transform.forward,out hit, dist)) { if (hit.collider.CompareTag("GrabBox")) { grabObject = hit.transform; grabObject.SetParent(grabSocket); grabObject.localPosition = Vector3.zero; grabObject.localRotation = Quaternion.identity; grabObject.GetComponent<Rigidbody>().isKinematic = true; } } } } }
PlayerCamera:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerCamera : MonoBehaviour { public Transform target; public float distance = 8.0f; public float height=-1.0f; // Start is called before the first frame update // Update is called once per frame void LateUpdate() { if (!target) return; transform.position = target.position; transform.position -= Vector3.forward * distance; transform.position = new Vector3(transform.position.x, height, transform.position.z); } }
AIController:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class AIController : MonoBehaviour { Character character; float lastCheckStateTime = 0; float simulateInputX; bool simulateJump; // Start is called before the first frame update void Start() { character = GetComponent<Character>(); } // Update is called once per frame void Update() { if (Time.time > lastCheckStateTime + 2) { lastCheckStateTime = Time.time; //模拟敌人的输入 simulateInputX = Random.Range(-1f, 1f); simulateJump = Random.Range(0, 2) == 1 ? true : false; } MoveControl(simulateInputX); JumpControl(simulateJump); } void MoveControl(float inputX) { character.Move(inputX); if (inputX != 0) { var dir = Vector3.right * inputX; character.Rotate(dir, 10); } } void JumpControl(bool jump) { if (jump) { character.Jump(); simulateJump = false; } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。