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,可以用它来统一管理当前活跃的方块,这样逻辑更清晰:
- 先创建一个单例
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(); } }
- 修改你的方块生成脚本(比如
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>(); } }
- 最后给按钮绑定事件:直接在按钮的
On Click()里选择GameManager.Instance的对应方法即可,不用额外的UI控制脚本。
方案3:用Unity事件系统解耦(进阶)
如果你想让代码解耦,避免直接引用,可以用Unity的事件系统:
- 给每个按钮创建事件脚本,比如
LeftMoveButton:
using UnityEngine; using UnityEngine.Events; public class LeftMoveButton : MonoBehaviour { public UnityEvent OnLeftMoveClicked; public void OnButtonClicked() { OnLeftMoveClicked.Invoke(); } }
- 修改
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




