今天今天小bug弄了我我半天,得了,这第四个游戏是半成品,不过也要把今天累计的经验和教训总结一下。
1、拉入场景,拉入新相机,拉入新角色
2、仔细一看,这次给的场景和我们做flappybird时候给的有异曲同工之妙,都是两块,再仔细想,这次我们也要执行拼接和背景的平移以完成道路一直有的任务。
3、其次拉入的任务要创建新的根节点装上去,并且创建collider和rigidbody,并提前把animator拉入我们的模型组件中。
4、调整collider,让其与游戏角色大小相等,把背景角色相机位置都初始化,这样有利于观察和debug
5、创建脚本,最基本的PlayerCharacter和PlayerController
6、第一步肯定就是要创建组件或脚本来得到当前物体的相应组件以调用脚本的方法
Rigidbody rigid;
Animator animator;
——》
private void Start()
{
rigid = GetComponent();
animator = GetComponentInChildren();
}
随后设置基本属性,跑步的初始速度,跑步的最大速度,和跑步的增量(加速度)
public float runSpeed = 5;
public const float runSpeedMax = 10f;
public const float runSpeedAcc = 5;
我们知道开始的的时候速度是5,那么之后怎么控制变化的速度和最后要是停止速度归零呢??
private void Update()
{
Vector3 velocityTemp = rigid.velocity;//得到当时刚体的速度
runSpeed += runSpeedAcc * Time.deltaTime;//按帧向上加加速度
runSpeed = Mathf.Clamp(runSpeed, 0f, runSpeedMax);//Mathf.Clamp(float value,float min,float max)限制第一个数的值在范围内
velocityTemp.z = runSpeed;//加后速度给予rigid控件
if (velocityTemp.y > 0f)
{
velocityTemp.y = 0f;//不能跳跃
}
//因为x娶不到,分别刷新yz轴速度之后进行合成给予
rigid.velocity = velocityTemp;
if (attackingTime>0)
{
attackingTime = attackingTime - Time.deltaTime;
}
}
可见我们需要创建新的三维向量来接收刚体每一帧的速度,由于是每一帧的刷新,先通过获取速度,再对runSpeed进行增加,步长为每一帧所占时间乘以加速度。由于这个速度在我们这个物理坐标系中微乎其微,我们可以当作这一帧变化很小
,之后再进行判断,这样更加严谨。如果超过了速度范围,会将速度限制再最大速度以内。之后再用刚体的运动实际速度进行接收。下面有一个对y轴速度的限制,其实我们可以锁定rigidbody中的y轴。再用刚体速度接收已经加工完毕的速度,其实完全可以直接对x轴速度进行调用和加工,不过我觉得如果只对一个轴的属性进行修改和调用是不利于后期维护的。
下面的attackingTime是用来限制animator的,下面我们先对于与之相关的Attack方法进行分析:
1)所需要变量:
public enum AttackMotion
{
Left,
Right
}
AttackMotion attackMotion = AttackMotion.Left;
public bool canAttack = true;
const float AttackTime = 0.3f;//攻击时间长短
public float attackingTime;//当前攻击的时间
整体代码:
public void Attack()
{
if (!canAttack)
{
return;
}
if (attackMotion == AttackMotion.Left)
{
animator.SetTrigger("AttackLeft");
swordEffectLeft.Play();
attackMotion = AttackMotion.Right;
}
else
{
animator.SetTrigger("AttackRight");
swordEffectRight.Play();
attackMotion = AttackMotion.Left;
}
attackingTime = AttackTime;
canAttack = false;
CancelInvoke("ResetCanAttack");
Invoke("ResetCanAttack", attackingTime + 1);//停顿一秒后才可以攻击
}
由于攻击方式分为左手和右手,这里我们直接用枚举类型变量来减少代码量。先进行初始化,第一次攻击是左手。再设置能否攻击,攻击一次需要的时间,最后需要设置计时器,即本次攻击时长,本次攻击时长就是通过攻击一次需要时间来限制的。
先判断能否攻击,再判断这次是左右手,判断成功后要设置下一次是不同手,让剑影动画播放,触发animator中的砍杀触发器。最后重置攻击时间。
这个一定要明天进行复习
Invoke(“方法名”,int);//int秒后调用名字为"方法名"的方法。
InvokeRepeating(“方法名”,int1,int2);//int1秒后调用调用名字为"方法名"的方法,之后每隔int2秒后再次调用这个方法。
CanceInvoke(string “方法名”) 取消这个脚本中所有的调用 取消这个MonoBehaviour上的所有调用。
Invokeing(“方法名”);有返回值返回bool型。正在调用就返回true
其中我们已经知道了 Invoke的用法,但在CancelInvoke中的脚本名字需要进行细说。
public void ResetCanAttack()
{
canAttack = true;
}
将能否攻击的bool类型变量进行重新赋值。强调一下,CancelInvoke是取消对方法在MonoBehaviour中的。由于每一次Update都有对Invoke方法的循环调用,这里为了在下一次方法的调用中免去上次的Invoke方法对下一次方法的意外调用。即这一次CancelInvoke的调用其实是服务于上一次的Invoke。
Character已经大体差不多了,看比较简单的Controller脚本:
public class PlayerController : MonoBehaviour
{
PlayerCharacter character;
private void Start()
{
character = FindObjectOfType();
}
void Update()
{
if (Input.GetMouseButtonDown(0)|Input.GetMouseButtonDown(1))
{
character.Attack();
}//我遇到了一个大问题,当然也可以说是一个小问题,模型初始位置在加上run动画后在游戏开始就错位
}
}
实例化一个Character并得到以便于调用方法,再在刷新函数中对鼠标是或否点击进行判断角色是否攻击(这个攻击函数就是Character脚本中的)
角色的跑动和攻击结束之后,需要设置场景的循环调用:
所需变量
const float width = 50f;//多长进行循环
const int backGroundNum = 2;//总共有俩块背景
//----------------------------------------
Transform mainCamera = null;//需要对相机的位置进行调用
Vector3 initPos;//初始化位置
初始化函数:
private void Start()
{
mainCamera = GameObject.FindGameObjectWithTag("MainCamera").transform;//GameObject.FindGameObjectsWithTag("MainCamera").transform;
//得到了这个GameObject中有MainCamera标签的脚本transform并赋值给mainCamera
initPos = transform.position;//取当前组件(背景)的开始位置为初始位置
}
在刷新函数中对背景位置进行调整:
private void Update()
{
float totalWidth = width * backGroundNum;//这里总长度等于单个长度乘以总背景数量
float distZ = mainCamera.position.z - initPos.z;//相机距离出初始位置在z轴距离
int n = Mathf.RoundToInt(distZ / totalWidth);//求走过多少个背景
var pos = initPos;//用pos来接收初始位置,这里也好进行相加
pos.z += n * totalWidth;//位置为原位置z坐标加上走过了n个背景的长度
transform.position = pos;//上面是通过对初始位置进行加工得到最终位置,这个pos就是加工之后得分最终位置
}
这里width其实等价于两个背景的BackGround.transform.position.z之差取绝对值,就是长度size。而相机是跟随角色移动的,相机的z轴坐标到背景position坐标就是人物距离background开始一端的距离。这里Mathf.RoundToInt意思
是四舍五入即由于整个场景是 由两个场景拼接在一起的,只要走了超过一半,即超过一个background长度,那个走过的background就可以移到后面。
我们在初始化函数中已经用initPos取到了初始位置,之后就要将这个变量当一个中间变量,取到z轴坐标加上走过的整数个背景长度,之后再赋值给当前控件的位置。其实仔细想一想,如果直接对于当前控件的位置作用也不是不行,但如果进行复杂的操作,参数不好修改和调整,这里引入一个类似形参的东西最后赋值给我们想要的控件属性就会减少一些bug也更好维护。
还有最复杂的关于怪物模型碰撞的函数,由于代码量有点大,我就自己在后台打了,而且原项目视频有的地方不清楚,难受。
怪物模型函数大大超出我的能力范围,我还需要更多基础的练习才能写出那样逻辑严谨,思路清晰
,分块明确的方法。好了,今天可算补完了昨天的博客。
。奥,对了,为什么模型的animator加入会影响模型在game中的视觉位置,偏离我们在根组件上设置的bollider。我也仔细观察过,游戏在开始和运行之前角色的y轴是没有变化的,因此,这种加animator之前正常奔跑,加之后飞起来,只有在攻击的时候下降的设定真的很让人蛋疼。模型偏离collider和animator有关么???肯定有关,但是animator的属性里面我也没看到对模型transform有影响的,这个一定要注意,给这个知识点整点绿色