【日志】2D横版动作游戏制作

发布于 2023-02-23  223 次阅读


ps:本来想写写开发教程,后来想了想,也没人看啊,就改成日志了。

教学地址:

油管:
https://www.youtube.com/@MSTUDIOHUB

西瓜视频:

https://www.ixigua.com/home/4265712447142734

其他可参考:

https://blog.csdn.net/weixin_64081689/article/details/129561283

正文:

人物碰撞(刚体)

创建一个角色,然后让他落在地面上,需要设置刚体和碰撞

刚体通俗易懂的说法就是重力,一个向下的力

选中人物后

在右边添加Rigidbody 2D组件,设置重力大小为1,这样该角色就会有一个向下的、为数值1的力,在运行程序的时候角色就会下落。

碰撞器

但是单单给一个向下的力,人物却不会落在地板上,因为人物与地面不会产生碰撞,就好像“穿模”。所以我们需要进行设置人物与地面的碰撞。

为了方面观察,我们先把背景图屏蔽掉

选择人物,添加Box Collider 2D组件,然后选择编辑碰撞器,这时候我们的角色就会出现一个可编辑的绿色框,这个框就是碰撞的范围。

画好碰撞的范围后再选择地面,与人物不同,地图的碰撞也有专门的组件,叫Tilemap Collider 2D,这个组件会把我们画的网格瓦片自动画上碰撞范围,这样人物与地形的碰撞就完成了。

点击试运行,小狐狸成功掉在地面上,说明成功了

人物的移动

人物已经能站在地形上了,但此时我们需要它能够动起来,这是我们需要新建一个脚本用于控制角色,右键选择新建一个组件,输入new 就可以看见新建新脚本的组件,选择新建,这个脚本是用于控制人物,所以我们可以取名为Player Controller,然后在Assets项目中就会出现刚刚新建的脚本Player Controller,为了规范化,我们在Assets中新建一个Scripts的文件夹专门用于装之后的脚本文件。把刚刚新建的player controller放进去。

打开脚本Player Controller

打开之后可以看见里面已经自动写好了两个方法void Start()和void Upate()。

简单介绍两个方法

void Start是当你开始播放游戏的时候(也就是运行游戏的时候),则开始运行里面的内容。

void Update则是已经播放之后,每一帧动画运行都会触发Update内的代码,比如我们之后要做的人物的跑动,就需要每帧更新,所以需要写在里面

接下来直接放完整的人物移动的代码。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    //rb刚体,speed速度
    public Rigidbody2D rb;
    public float speed;

    // Start is called before the first frame update
    void Start()
    {
        
        
    }

    // Update is called once per frame
    void Update()
    {
        Movement();
    }

    void Movement(){
        float horizontalmove;
        horizontalmove = Input.GetAxis("Horizontal");//获得移动项目
        if(horizontalmove != 0){
            rb.velocity = new Vector2(horizontalmove * speed, rb.velocity.y);//更改人物水平坐标
        }
    }
}

开头声明Rigidbody2D rb变量和一个 float speed,一个用来装人物移动后改变的位置,一个用来装移动的速度。

然后创建一个Movement方法,用来写人物的运动,然后每帧再调用该方法。

Movement方法内horizontalmove用于装人物的坐标(坐标改变说明人物正在移动)

所以如果horizontalmove 不等于0就说明输入设备改变人物的坐标(人物就需要移动)

此时就将人物的水平坐标(rb.velocity)进行更改。

这时候保存代码回到unity,这时候我们代码已经写完了,但是一个问题,虽然可以更改人物坐标来进行移动了,但是我们并每一个给rb赋值(没有拿到人物初始的坐标),所以我们回到unity后,在组件位置找到Player Controller中的rb,将Rifidbody 2D拖到player controller中的rb中。

然后再给速度赋值10,点击运行,此时使用键盘中的方向键就可以移动人物,但发现人物会翻跟头,回到

Rifidbody 2D中再Constrsints中把Z轴锁定。这样人物就不会翻跟头了。

移动卡顿失灵

人物已经可以可以移动和跳跃了
代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    //rb刚体,speed速度
    public Rigidbody2D rb;
    public float speed; //移动速度
    public float jumpforce;//跳跃的力

    // Start is called before the first frame update
    void Start()
    {
        
        
    }

    // Update is called once per frame
    
    void Update(){
         
    }
    void FixedUpdate()
    {
       Movement();
    }

    void Movement(){
        float horizontalmove = Input.GetAxis("Horizontal");//获取移动项目
        float facedircetion =Input.GetAxisRaw("Horizontal");
        if(horizontalmove != 0){

            rb.velocity = new Vector2(horizontalmove * speed * Time.deltaTime, rb.velocity.y);//更改人物水平坐标
        }
        //更改人物面部朝向
        if(facedircetion != 0){
            transform.localScale = new Vector3(facedircetion,1,1) ;
        }
        //更改人物垂直坐标,(跳跃)
        if(Input.GetButtonDown("Jump")){
            rb.velocity =new Vector2(rb.velocity.x,jumpforce * Time.deltaTime);
        }
    }
}

但是操作很不顺畅,不但有时候移动会突然不动,还会跳不起来。

一个一个问题进行测试;

原本因为根据麦扣老师所说为了让动起来的效果能适配在不同的电脑上

考虑到每个电脑的配置不同(“电脑与电脑的体质不能一概而论”)

所以原本是在Update()方法中调用的Movement()转变成了在FixedUpdate()中进行调用。

这里需要说一下Update()和FixedUpdate()的区别

Update():和实际时间有关,和游戏无关;是每一帧都调用一下,但是由于不同设备性能不一样,所以实际会根据实际性能不同而调用频率不一样。

FixedUpdate():和游戏时间有关,和实际实际无关;是一个固定的时间触发,实际为0.02秒。

也就说,FixedUpdate()每0.02秒执行一次。

那么我们之所以按下跳跃键角色没有跳起来说明很可能是FixedUpdate()漏掉了获取按键的帧。

简单说就是我们按下空格,但是此时FixedUpdate还没有到触发时间,所以这个动作没有被获取到,所以无事发生。

那么解决这个问题只需要把Movement()的调用换回到Update(),然后把水平移动和垂直移动的* Time.deltaTime删掉。

再次进行测试,发生已经没有之前那种明明按下跳跃却无法跳跃的情况了。

所以最后的结论就是

有关Input这类需要实时监听功能的需要放到Update中,而关于计算类型的最好放在FixedUpdate()之中。

移动卡段问题:

这个其实知道原理就很容易解决

首先我们看碰撞效果的边界线

原本小狐狸的碰撞边界显示一个box(盒型碰撞),而地图的碰撞也是网格

这样就有一个问题,地图的碰撞网格边界并不是真正的直线,而是一个一个方块并在一起的,

所以方块之间是有缝隙的,所以当小狐狸也是用box(盒子碰撞器)的时候两个方块可能会卡住

所以小狐狸的下班部分用圆形,上半部分有方块(或者也用圆形碰撞器)就可以了。

关于动画效果

制作动画效果的时候无法将图片精灵拖动到动画效果内

直接右键新建动画后,将图片拖入编辑窗内是无法成功拖入的

原因:需要选择相应的角色,然后再新建动画

先选择player,然后再动画控制器中的下拉框内选择新建动画

这样就能成功拖入

日志4.21

修改了原本的可操作模型,替换了原本的狐狸,但动作的改变也改变了原本踩死变成了攻击。

日志5.8

麻了,之前的替换的模型又暂时不用,又得换回来,等于白做

关于对话框(游戏提示)

人物再触碰到地图上的木牌的时候会进行提示,第一个指示牌会提示跳跃的使用

第二个可以提示可以用踩死的怪物的方法。

遇到问题:成功弹出第一个提示框后,第一个框消失,然后第二个提示出现

背景的提示框是同一个,只是文本不同。

我原本的想法是,在路牌上写脚本:

最开始的想法是

sing System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class sign : MonoBehaviour
{
    public GameObject enterDialog;
    public GameObject envirOnment;
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.tag =="Player")
        {
            // Debug.Log("成功碰撞了");
            Transform sign1 = envirOnment.transform.Find("sign1");
            Debug.Log("碰撞到了 " + collision.gameObject.name);
            if(sign1 != null){
                 enterDialog.SetActive(true);
            }
           
                
        }
    }
    
    private void OnTriggerExit2D(Collider2D collision)
    {
        if(collision.tag =="Player")
        {
            
            enterDialog.SetActive(false);
            Transform textTransform = enterDialog.transform.Find("Text");
            if (textTransform != null) 
            {
                textTransform.gameObject.SetActive(false);
            }
            

        }
    }
}


以上只是测试,一开始的每个木牌都写一个cs脚本控制,后来觉得那好像可以直接写一个父类木牌,然后见子类调用减少重复率,写一半的时候又猛然醒悟不如直接写在人物控制的脚本中,最后

 /**这是人物碰到不同的木牌的时候进行的提示**/
      if(collision.tag == "InteractiveItems")
      {
        Debug.Log("碰撞到了 " + collision.gameObject.name);
            if(collision.name == "sign1" ){
                enterDialog.SetActive(true);
               

            } 
            else if(collision.name == "sign2"){
                
                enterDialog.SetActive(true);
                文本框.text ="通过跳跃可以踩死敌人。";
               
            }
        

直接将这段写到了人物收集的方法中