6. 开始游戏,生成主角用摇杆控制移动

专栏收录该内容

Hi I'm Shendi



这节开始制作游戏交互部分,在此之前先更改一下背景方块生成速度

Background代码更改

在之前使用的 InvokeRepeating 函数来生成方块,这种方式是固定一定时间创建方块,每次打开游戏时随机的时间就固定了,所以就造成了有时打开游戏,方块生成快,有时打开,方块生成慢


于是改用协程来实现

/** 单例 */
public static Background _instance;
private void Awake()
{
    _instance = this;
}

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

/** 创建方块,三秒后销毁 */
IEnumerator CreateRanBox()
{
    while (true)
    {
        // 等待随机时间
        yield return new WaitForSeconds(Random.Range(0.5f, 1.5f));

        // 创建方块
        if (State.isBackgroundPlay) {
            var obj = State.CreateRanBox();
            obj.transform.parent = transform;
            obj.transform.localPosition = Vector2.zero;

            Rigidbody rigidbody = obj.AddComponent<Rigidbody>();
            rigidbody.velocity = Vector3.down * Random.Range(0.1f, 2f);

            Destroy(obj, 2f);
        }
        else
        {
            break;
        }
    }
}

以上可以改可以不改,只是一个小插曲,Awake在对象被初始化时执行,执行的顺序会在Start函数之前

上面使用了死循环,在 State.isBackgroundPlay 为false时会跳出死循环

后面做开始游戏时会停止背景,游戏结束要重新执行背景,所以使用单例将对象提供出去

如果使用之前那种方法,后面控制背景的开关只需要设置 State.isBackgroundPlay 即可



开始游戏按钮操作

第一步当然是要编写点击开始游戏按钮事件的处理操作


增加点击事件

打开之前新建的脚本 Main,在里面新增一个函数命名为 StartGame,代表启动游戏

public class Main : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {

    }

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

    /** 开始游戏点击 */
    public void StartGame()
    {
        Debug.Log("click");
    }

}

这个脚本在之前是添加到了主摄像机上

在Unity内点击开始游戏按钮,在右边找到Button组件,展开,找到 鼠标单击,点击小圆圈,选择主摄像机

按钮组件



点击右边的No Function,选择 Main - StartGame ()

按钮点击事件


这样点击按钮时就会触发 Main 脚本内的 StartGame 函数了,运行点击一下,会发现函数执行了

输出



思路

从目前的角度出发,点击开始游戏按钮后应

  • 初始化一些数值等
  • 停止背景的播放
  • 隐藏主页,显示游戏页面
  • 创建角色到游戏界面
  • ...


停止背景播放

之前我们使用了一个变量来做这个功能,将变量状态改变即可

// 停止背景播放
State.isBackgroundPlay = false;

运行,点击开始游戏按钮,即可看到没有方块往下掉落了



隐藏主页,显示游戏页面


首先创建一个空对象,将主页的一些内容都放到空对象内

层级面板


再创建一个空对象,作为游戏内容的父对象,命名例如 PageMain,PageGame


然后在 Main 脚本内创建两个公开变量

public class Main : MonoBehaviour
{
    public GameObject pageMain;
    public GameObject pageGame;
}

保存后可以在Unity的摄像机内的Main组件处看到 pageMain 和 pageGame 的框框,在层级面板将PageMain和PageGame分别拖入到对应的框框(或者点击框框右边的圆圈直接选择)

Main脚本



接下来编写代码,使用游戏对象的SetActive函数来控制显示隐藏,隐藏 pageMain,显示 pageGame 即可

/** 开始游戏点击 */
public void StartGame()
{
    // 停止背景播放
    State.isBackgroundPlay = false;

    // 隐藏main显示game
    pageMain.SetActive(false);
    pageGame.SetActive(true);
}


在层级面板点击对象,右边检查器有个勾选框,取消勾选则可以默认就隐藏

隐藏


通常还会记录游戏的状态,例如是否开始游戏,是否暂停游戏,于是在 State 中添加两个属性

/** 游戏是否开始 */
public static bool isGameStart = false;
/** 游戏是否暂停 */
public static bool isGamePause = false;

在 StartGame 中将 isGameStart 置为 true 即可



实例化主角

将主角的预制体同之前小方块一样,在State类中加载

/** 角色 */
public static GameObject player = Resources.Load<GameObject>("Prefabs/Player");

除此之外,可以把角色实例化和销毁的部分封装成函数放到State中

#region 角色部分
/** 角色实例 */
public static GameObject playerInstance;
#endregion

/** 角色初始化,初始化完成后 playerInstance 将为实例化的角色 */
public static GameObject PlayerInit()
{
    playerInstance = GameObject.Instantiate(player);
    return playerInstance;
}

/** 角色销毁 */
public static void PlayerDestory()
{
    if (playerInstance != null)
    {
        GameObject.Destroy(playerInstance);
        playerInstance = null;
    }
}


在开始游戏按钮处理处增加调用即可实例化主角

// 实例化主角
State.PlayerInit().transform.parent = pageGame.transform;

运行游戏,点击开始游戏按钮,可以看到角色在左下角生成了

角色



让角色动起来


摇杆的制作

UI分为两部分

  1. 摇杆的背景(最外层的圈圈)
  2. 摇杆中心可拖动的圆点

在 PageGame 处创建 UI - 图像,然后在创建的图像下继续创建一个图像


点击最外层的图像,点击右边源图像右边的圆圈,发现有一个圆圈的图片,选择使用

选择图


更改一下颜色透明度和大小,将其拖动到左下角(按Shift+鼠标拖拽大小可以等比缩放)

然后在层级面板选择图像下的图像,同样设置一样的源图像,调整一下大小和颜色,效果如下

摇杆效果图


层级面板如下

层级面板



摇杆的脚本

创建一个脚本名为 Rocker,将脚本拖到层级面板的Rocker上,然后编写代码

首先,将以下两个using语句添加到脚本头部:

using UnityEngine.UI;
using UnityEngine.EventSystems;

这两个命名空间中包含摇杆中用到的UI元素和事件系统。


然后,在类定义前添加以下代码:

public class Rocker: MonoBehaviour, IDragHandler, IPointerUpHandler, IPointerDownHandler

这个代码意味着该脚本实现IDragHandler、IPointerUpHandler、IPointerDownHandler接口,其中IDragHandler接口处理拖放事件,IPointerUpHandler接口处理放手事件,IPointerDownHandler接口处理按下事件。

接着,在类定义内添加以下成员变量:

public Image rockerImg;
public Image rockerCenterImg;

在 Start 中初始化变量

void Start()
{
    rockerImg = GetComponent<Image>();
    rockerCenterImg = transform.GetChild(0).GetComponent<Image>();
}

接下来处理 OnPointerDown 按下事件,想实现的效果是默认摇杆应该是隐藏的,等在哪点击就在哪出现,隐藏可以直接在Unity中取消勾选即可

但是会发现上面三个事件只有在点击摇杆元素时才会触发

于是需要改动一下,新增一个UI图片元素,作为拖拽区域

拖拽区域即在这一块点击时可以触发摇杆操作

将颜色设为透明,然后调整想要的区域大小

摇杆

将Rocker上的脚本拖到RockerPanel,接下来继续编写代码

Start的改动

void Start()
{
    rockerImg = transform.GetChild(0).GetComponent<Image>();
    rockerCenterImg = transform.GetChild(0).GetChild(0).GetComponent<Image>();
}

当按下时,显示摇杆,并记录和初始化位置

/** 摇杆第一次点击的位置 */
private Vector2 firstPos;

// 处理按下事件
public void OnPointerDown(PointerEventData eventData)
{
    // eventData.position是按下的位置
    rockerImg.transform.position = eventData.position;
    firstPos = eventData.position;

    OnDrag(eventData);

    rockerImg.gameObject.SetActive(true);
}

其中的 OnDrag 是拖拽时触发的函数,调用是为了初始化摇杆中心的小点位置


当抬起(按下后松手)时,隐藏摇杆

// 处理按下后放手事件
public void OnPointerUp(PointerEventData eventData)
{
    rockerImg.gameObject.SetActive(false);
    rockerCenterImg.transform.position = Vector2.zero;
}

剩下的就是拖拽了,因为我们的脚本绑在拖拽区域上,所以要以摇杆的位置为中心

将拿到的位置要去掉按下的位置就可以了,然后将位置归一化,最后限制小圆点的位置不超出外层圆圈位置

// 处理拖放事件
public void OnDrag(PointerEventData eventData)
{
    Vector2 position = Vector2.zero;
    // 当前位置为点击位置减去初始位置
    var curPos = new Vector2(eventData.position.x - firstPos.x, eventData.position.y - firstPos.y);

    curPos = (curPos.magnitude > 1) ? curPos.normalized : curPos;
    // 限制能移动的区域
    rockerCenterImg.rectTransform.anchoredPosition = new Vector2(curPos.x * (rockerImg.rectTransform.sizeDelta.x / 3), curPos.y * (rockerImg.rectTransform.sizeDelta.y) / 3);
}

在这里还需要将当前位置保存起来或提供出去,并且在抬起时重置

// 当前摇杆拖拽位置
public static Vector2 curPos;

public void OnDrag(PointerEventData eventData) {
    // ...
	Rocker.curPos = curPos;
}

public void OnPointerUp(PointerEventData eventData) {
    // ...
    Rocker.curPos = Vector2.zero;
}


角色的脚本

打开Player脚本,在代码中通过摇杆脚本提高的位置来更改角色的位置

分析一下位置信息,根据之前的策划,角色默认可以移动两方块/秒,那么就是1/s,使用固定的调用函数 FixedUpdate,一秒执行五十次,那么每次移动 1/50=0.02

将这些信息放到 State 中:

/** 小方块的大小 */
public static float boxSize = box.GetComponent<Renderer>().bounds.size.x;

Player 脚本中记录角色速度

/** 每次的角色速度,两个小方块大小,每秒执行50次 */
public float playerSpeed;

private void Awake()
{
    playerSpeed = State.boxSize * 2 / 50;
}

每次执行判断摇杆是否有操作,有则判断移动方向,摇杆往上代表跳跃(y>0.7)

首先做左右部分,x负数为左,正数为右

void FixedUpdate()
{
    var curPos = Rocker.curPos;
    if (curPos.magnitude != 0)
    {
        if (curPos.x < 0)
        {
            transform.position = new Vector3(transform.position.x - playerSpeed, transform.position.y);
        }
        else if (curPos.x > 0)
        {
            transform.position = new Vector3(transform.position.x + playerSpeed, transform.position.y);
        }

    }
}

运行后,开始游戏,即可控制角色左右移动了

角色移动效果图



本文链接:https://sdpro.top/blog/html/article/1065.html

♥ 赞助 ♥

尽管去做,或许最终的结果不尽人意,但你不付出,他不付出,那怎会进步呢?