从零开始的RPG制作(2.1-人物控制)
2019-04-25 本文已影响55人
小蜻蜓队长
在上一张内容中我们设计了基础结构,现在我们来开始实现人物控制。首先我们去找一个漂亮的小姐姐来做我们的模特。
小姐姐照片
我们就用这个小姐姐了。(非商业,大家可以去网上找找 Eri,这里就不贴出链接了~)
i场景接着我们在场景中拉出几块cube 和 一个plane ,把小姐姐放进去。
1.创建Update控制器
我们不妨先思考一下,角色控制器肯定是需要时序每一帧都接收用户输入的信号的,那么自然而然的就会想到在update里面进行操作,然后就在角色脚本的update的函数里开始写,这种做法太过粗旷,我们的游戏,只需要一组Update就行了,其他需要Update的东西放入那里面就行,那具体如果做了,下面贴码。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UpdateManges:MonoBehaviour {
public delegate void UpdateManage();
static event UpdateManage playerEventList_LateUpdate;//人物事件需要放入lateUpdate处理的。
static event UpdateManage playerEventList_FixUpdate;//人物事件需要放入FixUpdate处理的。
static event UpdateManage playerEventList_Update;//人物事件需要放入Update处理的。
#region 事件的删除与添加
public static void add_playerEventList_Late(UpdateManage updatemanage) {
if (updatemanage != null)
playerEventList_LateUpdate += updatemanage;
}
public static void sub_playerEventList_Late(UpdateManage updatemanage) {
if (updatemanage != null)
playerEventList_LateUpdate -= updatemanage;
}
public static void add_playerEventList_Fix(UpdateManage updatemanage) {
if (updatemanage != null)
playerEventList_FixUpdate += updatemanage;
}
public static void sub_playerEventList_Fix(UpdateManage updatemanage) {
if (updatemanage != null)
playerEventList_FixUpdate -= updatemanage;
}
public static void add_playerEventList_(UpdateManage updatemanage) {
if (updatemanage != null)
playerEventList_Update += updatemanage;
}
public static void sub_playerEventList_(UpdateManage updatemanage) {
if (updatemanage != null)
playerEventList_Update -= updatemanage;
}
#endregion
private void Update() {
if (playerEventList_Update != null) {
playerEventList_Update();
}
}
private void LateUpdate() {
if (playerEventList_LateUpdate != null) {
playerEventList_LateUpdate();
}
}
private void FixedUpdate() {
if (playerEventList_FixUpdate != null) {
playerEventList_FixUpdate();
}
}
}
应该还是比较清晰的,外部就通过这几个静态函数调用就可以,然后我们把我们的码子丢道一个GameObject,取名为UpdateManage。
2.小姐姐的基础配置。
首先我们创建一个空物体称之为Player,将PlayerTestView丢在这个Player上,接着将小姐姐放入这个Player,将小姐姐身上挂着的东西全部remove掉,接着我们在Player挂上一个Capsule Collider 和 一个 Rigidbody。
当这步配置结束之后,我们创建一个 Animator命名为 playerAnim。然后将小姐姐的骨骼绑定在上面。
配置
接着配置Animator。
我们在playerAnim中创建一个1D混合树,用于混合小姐姐静默状态,走路状态,跑步状态。
配置如图
接着设置一个walk的变量来作为1D混合书的混合参数。
配置如图
接着我们就要回到角色控制最为关键的两个类用于显示的PlayerTestView和用于操作的PlayerTestMediator。
3.创建输入信号接受函数,PlayerTestMediator中的关键函数keyController。
using strange.extensions.mediation.impl;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerTestMediator:Mediator {
// Use this for initialization
//test
float up = 0;
float right = 0;
float upVelocity = 0;
float rightVelocity = 0;
Vector3 vec;
[Inject]
public PlayerTestView playerView { get; set; }
public override void OnRegister() {//绑定成功之后会调用这个API
Debug.Log("OnRegister");
playerView.init();
UpdateManges.add_playerEventList_(keyController);
}
public override void OnRemove() {//接触绑定之后调用这个API
Debug.Log("OnRemove");
UpdateManges.sub_playerEventList_(keyController);
}
bool run = false;
bool jump = false;
void keyController() {
///用wasd来控制前进与否
float targetup = (Input.GetKey("w") ? 1 : 0) - (Input.GetKey("s") ? 1 : 0);
float targetright = (Input.GetKey("d") ? 1 : 0) - (Input.GetKey("a") ? 1 : 0);
up = Mathf.Lerp(up, targetup,0.6f);
right = Mathf.Lerp(right, targetright, 0.6f);
if (Input.GetKeyDown(KeyCode.LeftShift)) {//用LeftShift切换奔跑
run = !run;
}
jump = Input.GetKey(KeyCode.Space);//用Space来切换跳跃。
}
}
4.控制端暂且如此,现在我们去初始化PlayerTestView,也就是视觉表现层的组件。
struct PlayerAnimaId {
public int baseAniam;//1D混合数的 ID号。
};
//将PlayerTestView需要用到的东西提前准备好
[Header("===== 组件 ====")]
Rigidbody rig;
Animator animator;
CapsuleCollider capsuleCollider;
Transform player;
[Header("===== 数据 ====")]
float addSpeed = 1;//需要增加的速度,比如给人攻击推了一下
float runSpeed = 3;//正常的跑步速度。
float walkSpend = 1;//正常的走路速度。
Vector3 moveDirection;//用于最终设置的参数,所有的移动变化都存储于此。
public void init() {
player = transform;
rig = GetComponent<Rigidbody>();
animator = GetComponent<Animator>();
capsuleCollider = GetComponent<CapsuleCollider>();
pad = new PlayerAnimaId();
pad.baseAniam = Animator.StringToHash("walk");
}
现在我们把改准备的都准备,开始启动小姐姐移动的编写,我们这里设置为函数AnimaBlendAction
void animaBlendAction(float right, float forward, bool run) {//设置转向以及速度
Vector3 vec = (right * Vector3.right + forward * Vector3.forward);//设置方向
float distance = vec.magnitude;
if (distance < Vector3.kEpsilon) {//停下
distance = 0;
lerpSpeed = Mathf.Lerp(lerpSpeed, 0, 0.2f);//速度逐渐归零。
} else {//开始行动
player.forward = Vector3.Slerp(player.forward, vec, 0.2f);//设置转向
float upperLimit = run ? runSpeed : walkSpend;
lerpSpeed = Mathf.Lerp(lerpSpeed, upperLimit, 0.1f);//速度逐渐上升。
}
animator.SetFloat(pad.baseAniam, lerpSpeed);//控制人物动作
moveDirection = Vector3.Project(moveDirection, Vector3.up);//每一次执行都要清理一边,但是需要保留起跳速度,我们这里先用 Vector3.up暂时用一下,代表角色每一次都是朝正上方起跳,如果后面需要斜面墙体什么的,到时候在将 Vector3.up替换掉。
moveDirection += player.forward * lerpSpeed * addSpeed;
}
public void playerMove() {//这里是人物移动的最终执行,你们可以看到这里我写了两种方式,你们可以尝试一下。会有什么不同,
//player.position += moveDirection ;//直接设置位置
//rig.velocity = Vector3.zero//直接设置位置会和碰撞体产生不必要的加速度,我们不需要这个加速度。
rig.velocity = moveDirection;//设置刚体速度
}
5.这里做好了然后,我们只需要去PlayerTestMediator调用即可。
public override void OnRegister() {//绑定成功之后会调用这个API
Debug.Log("OnRegister");
playerView.init();
UpdateManges.add_playerEventList_(keyController);
UpdateManges.add_playerEventList_(playerAction);
}
public override void OnRemove() {//接触绑定之后调用这个API
Debug.Log("OnRemove");
UpdateManges.sub_playerEventList_(keyController);
UpdateManges.sub_playerEventList_(playerAction);
}
void playerAction() {
playerView.baseWalkRun(right, up, run);
playerView.playerMove();
}
最终————
参考