You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Unity安卓俄罗斯方块:如何用屏幕按钮控制随机生成的Tetromino?

解决Unity俄罗斯方块屏幕按钮访问活跃方块脚本的问题

嘿,刚好我之前在Unity里做类似的方块游戏时遇到过一模一样的问题,给你几个简单易行的解决方案,都是在Unity 2017里能直接用的:

方案1:给方块控制脚本添加单例模式

这是最直接的方法,让按钮能随时找到当前活跃的方块实例:

首先修改你的TetrominoController脚本,添加静态单例引用:

using UnityEngine;

public class TetrominoController : MonoBehaviour
{
    // 静态实例,全局可访问
    public static TetrominoController CurrentActiveTetromino;

    void Awake()
    {
        // 每次生成新方块时,替换旧的活跃方块引用
        if (CurrentActiveTetromino != null)
        {
            // 如果旧方块还存在(比如没落地),这里可以根据逻辑处理,比如清空引用
            CurrentActiveTetromino = null;
        }
        CurrentActiveTetromino = this;
    }

    // 你的移动/旋转方法,改成public供按钮调用
    public void MoveLeft()
    {
        // 你的左移逻辑代码
        transform.Translate(Vector3.left);
    }

    public void MoveRight()
    {
        transform.Translate(Vector3.right);
    }

    public void RotateTetromino()
    {
        // 你的旋转逻辑代码
        transform.Rotate(0, 0, 90);
    }

    void OnDestroy()
    {
        // 方块销毁时清空引用,避免空指针错误
        if (CurrentActiveTetromino == this)
        {
            CurrentActiveTetromino = null;
        }
    }
}

然后给屏幕按钮添加调用逻辑:

  • 创建一个简单的UI控制脚本(比如GameUIController),挂在你的UI画布上:
using UnityEngine;

public class GameUIController : MonoBehaviour
{
    public void OnLeftButtonClick()
    {
        if (TetrominoController.CurrentActiveTetromino != null)
        {
            TetrominoController.CurrentActiveTetromino.MoveLeft();
        }
    }

    public void OnRightButtonClick()
    {
        if (TetrominoController.CurrentActiveTetromino != null)
        {
            TetrominoController.CurrentActiveTetromino.MoveRight();
        }
    }

    public void OnRotateButtonClick()
    {
        if (TetrominoController.CurrentActiveTetromino != null)
        {
            TetrominoController.CurrentActiveTetromino.RotateTetromino();
        }
    }
}
  • 在Unity编辑器中,选中你的屏幕按钮,在On Click()事件里,拖拽挂载了GameUIController的对象,然后选择对应的方法(比如OnLeftButtonClick)。

方案2:用游戏管理器跟踪活跃方块

如果你的项目已经有GameManager,可以用它来统一管理当前活跃的方块,这样逻辑更清晰:

  1. 先创建一个单例GameManager
using UnityEngine;

public class GameManager : MonoBehaviour
{
    public static GameManager Instance;
    // 存储当前活跃的方块控制脚本
    public TetrominoController CurrentActiveTetromino;

    void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }

    // 供按钮调用的封装方法
    public void MoveCurrentTetrominoLeft()
    {
        CurrentActiveTetromino?.MoveLeft();
    }

    public void MoveCurrentTetrominoRight()
    {
        CurrentActiveTetromino?.MoveRight();
    }

    public void RotateCurrentTetromino()
    {
        CurrentActiveTetromino?.RotateTetromino();
    }
}
  1. 修改你的方块生成脚本(比如TetrominoSpawner),在生成新方块时给GameManager赋值:
using UnityEngine;

public class TetrominoSpawner : MonoBehaviour
{
    public GameObject[] TetrominoPrefabs;

    public void SpawnNewTetromino()
    {
        int randomIndex = Random.Range(0, TetrominoPrefabs.Length);
        GameObject newTetromino = Instantiate(TetrominoPrefabs[randomIndex], transform.position, Quaternion.identity);
        
        // 告诉GameManager当前活跃的方块是哪一个
        GameManager.Instance.CurrentActiveTetromino = newTetromino.GetComponent<TetrominoController>();
    }
}
  1. 最后给按钮绑定事件:直接在按钮的On Click()里选择GameManager.Instance的对应方法即可,不用额外的UI控制脚本。

方案3:用Unity事件系统解耦(进阶)

如果你想让代码解耦,避免直接引用,可以用Unity的事件系统:

  1. 给每个按钮创建事件脚本,比如LeftMoveButton
using UnityEngine;
using UnityEngine.Events;

public class LeftMoveButton : MonoBehaviour
{
    public UnityEvent OnLeftMoveClicked;

    public void OnButtonClicked()
    {
        OnLeftMoveClicked.Invoke();
    }
}
  1. 修改TetrominoController,让它订阅按钮的事件:
using UnityEngine;

public class TetrominoController : MonoBehaviour
{
    private LeftMoveButton _leftButton;
    private RightMoveButton _rightButton;
    private RotateButton _rotateButton;

    void Start()
    {
        // 找到场景中的按钮组件
        _leftButton = FindObjectOfType<LeftMoveButton>();
        _rightButton = FindObjectOfType<RightMoveButton>();
        _rotateButton = FindObjectOfType<RotateButton>();

        // 订阅事件
        _leftButton.OnLeftMoveClicked.AddListener(MoveLeft);
        _rightButton.OnRightMoveClicked.AddListener(MoveRight);
        _rotateButton.OnRotateClicked.AddListener(RotateTetromino);
    }

    // 你的移动/旋转方法
    private void MoveLeft() { /* 逻辑 */ }
    private void MoveRight() { /* 逻辑 */ }
    private void RotateTetromino() { /* 逻辑 */ }

    void OnDestroy()
    {
        // 取消订阅,防止内存泄漏
        _leftButton.OnLeftMoveClicked.RemoveListener(MoveLeft);
        _rightButton.OnRightMoveClicked.RemoveListener(MoveRight);
        _rotateButton.OnRotateClicked.RemoveListener(RotateTetromino);
    }
}

这个方案适合大型项目,不过对于俄罗斯方块来说,前两种方案已经足够简单好用了。

内容的提问来源于stack exchange,提问作者Tony Williams

火山引擎 最新活动