第11章 基于HTC VIVE的沉浸式俄罗斯方块开发实践

11.1 设计理念与交互流程解析

11.1.1 虚拟现实环境下的游戏设计哲学

在传统游戏VR化的过程中,俄罗斯方块作为一个经典的二维游戏,其转化为三维沉浸式体验面临着独特的设计挑战。VR环境中的俄罗斯方块不再仅仅是屏幕上的图形排列,而是转变为玩家可物理交互的空间实体。这种转变要求开发者重新思考游戏的核心机制:重力方向从传统的垂直下落转变为三维空间中的向量运动,方块旋转从二维平面操作升级为六自由度空间变换。

在商业VR游戏开发中,必须考虑人机工程学因素。玩家在佩戴HTC VIVE头显时,连续游戏时间通常在30-90分钟之间,因此交互设计必须避免颈部疲劳和手臂酸痛。我们的解决方案是将游戏区域设置在玩家自然视线下方15-30度范围,符合人体自然俯视角度。方块操作区域距离玩家身体约0.6-0.8米,这是手臂最舒适的操作距离。

空间音频在VR俄罗斯方块中扮演着重要角色。每个方块碰撞、旋转和消除都应具有明确的空间定位感,帮助玩家在不依赖视觉的情况下感知游戏状态。通过SteamVR Audio的HRTF技术,我们可以实现真实的三维音频体验,当方块在玩家左侧消除时,声音应该明确从左耳传来。

using UnityEngine;
using System.Collections.Generic;

namespace VR.Tetris.Core
{
    /// <summary>
    /// 三维俄罗斯方块游戏核心管理器
    /// 商业项目中负责协调所有游戏子系统
    /// </summary>
    public class VRTetrisGameManager : MonoBehaviour
    {
        // 单例模式确保全局访问
        private static VRTetrisGameManager instance;
        public static VRTetrisGameManager Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = FindObjectOfType<VRTetrisGameManager>();
                }
                return instance;
            }
        }
        
        // 游戏状态枚举
        public enum GameState
        {
            Initializing,
            MainMenu,
            Playing,
            Paused,
            GameOver,
            Leaderboard
        }
        
        private GameState currentState = GameState.Initializing;
        private int currentScore = 0;
        private int currentLevel = 1;
        private float gameSpeed = 1.0f;
        
        // 游戏配置参数
        [SerializeField]
        private GameConfiguration gameConfig;
        
        // 方块生成器引用
        private BlockSpawner blockSpawner;
        
        // 空间音频管理器
        private SpatialAudioManager audioManager;
        
        /// <summary>
        /// 初始化游戏系统
        /// </summary>
        private void InitializeGameSystems()
        {
            // 验证必要组件
            if (gameConfig == null)
            {
                Debug.LogError("游戏配置未设置!");
                return;
            }
            
            // 初始化子系统
            blockSpawner = GetComponent<BlockSpawner>();
            if (blockSpawner != null)
            {
                blockSpawner.Initialize(gameConfig.BlockPrefabs);
            }
            
            // 初始化音频系统
            audioManager = SpatialAudioManager.Instance;
            if (audioManager != null)
            {
                audioManager.ConfigureAudioForVR();
            }
            
            // 加载玩家数据
            LoadPlayerProgress();
            
            // 切换到主菜单状态
            ChangeGameState(GameState.MainMenu);
        }
        
        /// <summary>
        /// 更改游戏状态
        /// </summary>
        public void ChangeGameState(GameState newState)
        {
            GameState previousState = currentState;
            currentState = newState;
            
            // 执行状态转换逻辑
            OnGameStateChanged(previousState, newState);
            
            // 商业项目中通常会触发分析事件
            LogGameStateChange(previousState, newState);
        }
        
        /// <summary>
        /// 游戏状态变更处理
        /// </summary>
        private void OnGameStateChanged(GameState oldState, GameState newState)
        {
            switch (newState)
            {
                case GameState.Playing:
                    StartGameSession();
                    break;
                    
                case GameState.Paused:
                    PauseGameSystems();
                    break;
                    
                case GameState.GameOver:
                    EndGameSession();
                    break;
                    
                case GameState.Leaderboard:
                    DisplayLeaderboard();
                    break;
            }
        }
        
        /// <summary>
        /// 开始游戏会话
        /// </summary>
        private void StartGameSession()
        {
            currentScore = 0;
            currentLevel = 1;
            gameSpeed = gameConfig.InitialGameSpeed;
            
            // 更新UI
            UIManager.Instance.UpdateScore(currentScore);
            UIManager.Instance.UpdateLevel(currentLevel);
            
            // 生成第一个方块
            if (blockSpawner != null)
            {
                blockSpawner.SpawnNextBlock();
            }
            
            // 开始背景音乐
            if (audioManager != null)
            {
                audioManager.PlayBackgroundMusic(AudioType.Gameplay);
            }
            
            // 商业项目:记录游戏开始事件
            AnalyticsManager.LogEvent("game_started", new Dictionary<string, object>
            {
                { "level", currentLevel },
                { "player_id", PlayerProfile.Instance.PlayerId }
            });
        }
        
        /// <summary>
        /// 加载玩家进度
        /// </summary>
        private void LoadPlayerProgress()
        {
            // 商业项目中通常使用加密的本地存储或云存储
            string playerDataJson = PlayerPrefs.GetString("PlayerProgress", "");
            if (!string.IsNullOrEmpty(playerDataJson))
            {
                // 反序列化玩家数据
                PlayerProgressData progressData = 
                    JsonUtility.FromJson<PlayerProgressData>(playerDataJson);
                
                // 应用加载的数据
                ApplyPlayerProgress(progressData);
            }
        }
        
        /// <summary>
        /// 保存玩家进度
        /// </summary>
        private void SavePlayerProgress()
        {
            PlayerProgressData progressData = new PlayerProgressData
            {
                HighScore = GetHighScore(),
                TotalPlayTime = GetTotalPlayTime(),
                UnlockedAchievements = GetUnlockedAchievements()
            };
            
            string progressJson = JsonUtility.ToJson(progressData);
            PlayerPrefs.SetString("PlayerProgress", progressJson);
            PlayerPrefs.Save();
        }
        
        // 其他游戏管理方法...
    }
    
    /// <summary>
    /// 游戏配置数据结构
    /// </summary>
    [System.Serializable]
    public class GameConfiguration
    {
        public float InitialGameSpeed = 1.0f;
        public float SpeedIncreasePerLevel = 0.1f;
        public int ScorePerLine = 100;
        public int ScorePerTetris = 800;
        public GameObject[] BlockPrefabs;
        public Vector3 PlayAreaCenter = Vector3.zero;
        public Vector3 PlayAreaSize = new Vector3(5, 10, 5);
    }
    
    /// <summary>
    /// 玩家进度数据结构
    /// </summary>
    [System.Serializable]
    public class PlayerProgressData
    {
        public int HighScore;
        public float TotalPlayTime;
        public List<string> UnlockedAchievements;
    }
}

11.1.2 交互机制与用户体验设计

VR环境中的俄罗斯方块操作需要重新设计传统控制方案。HTC VIVE手柄提供了6DOF(六自由度)输入能力,允许玩家以自然手势与虚拟方块交互。我们设计了三种主要交互模式:抓取-放置模式、指向-选择模式、手势识别模式。

在商业VR项目中,输入反馈的即时性至关重要。我们采用了多层次反馈系统:

  1. 触觉反馈:使用HTC VIVE控制器的触觉马达,在方块抓取、旋转、放置时提供不同强度的震动
  2. 视觉反馈:方块被选中时显示高亮轮廓,可放置位置显示半透明预览
  3. 音频反馈:每个操作都有对应的空间音频反馈
using UnityEngine;
using Valve.VR;
using System.Collections;

namespace VR.Tetris.Interaction
{
    /// <summary>
    /// HTC VIVE手柄交互控制器
    /// 处理所有手柄输入和反馈
    /// </summary>
    public class ViveControllerHandler : MonoBehaviour
    {
        // SteamVR动作引用
        public SteamVR_Action_Boolean grabAction;
        public SteamVR_Action_Boolean rotateAction;
        public SteamVR_Action_Boolean menuAction;
        public SteamVR_Action_Vector2 touchpadAction;
        
        // 触觉反馈设置
        [SerializeField]
        private HapticFeedbackSettings hapticSettings;
        
        // 当前手持的对象
        private BlockController heldBlock;
        private bool isHoldingBlock = false;
        
        // 手柄设备
        private SteamVR_Behaviour_Pose controllerPose;
        private SteamVR_Input_Sources inputSource;
        
        // 射线交互组件
        private LineRenderer laserPointer;
        private bool isLaserActive = false;
        
        /// <summary>
        /// 初始化控制器
        /// </summary>
        private void Start()
        {
            controllerPose = GetComponent<SteamVR_Behaviour_Pose>();
            if (controllerPose != null)
            {
                inputSource = controllerPose.inputSource;
            }
            
            // 初始化激光指针
            InitializeLaserPointer();
            
            // 注册输入事件
            RegisterInputEvents();
        }
        
        /// <summary>
        /// 初始化激光指针
        /// </summary>
        private void InitializeLaserPointer()
        {
            laserPointer = gameObject.AddComponent<LineRenderer>();
            laserPointer.startWidth = 0.01f;
            laserPointer.endWidth = 0.005f;
            laserPointer.material = new Material(Shader.Find("Unlit/Color"))
            {
                color = Color.cyan
            };
            laserPointer.enabled = false;
        }
        
        /// <summary>
        /// 注册输入事件
        /// </summary>
        private void RegisterInputEvents()
        {
            if (grabAction != null)
            {
                grabAction.AddOnStateDownListener(OnGrabPressed, inputSource);
                grabAction.AddOnStateUpListener(OnGrabReleased, inputSource);
            }
            
            if (rotateAction != null)
            {
                rotateAction.AddOnStateDownListener(OnRotatePressed, inputSource);
            }
            
            if (menuAction != null)
            {
                menuAction.AddOnStateDownListener(OnMenuPressed, inputSource);
            }
        }
        
        /// <summary>
        /// 抓取按钮按下
        /// </summary>
        private void OnGrabPressed(SteamVR_Action_Boolean action, SteamVR_Input_Sources source)
        {
            if (!isHoldingBlock)
            {
                // 尝试抓取方块
                TryGrabBlock();
            }
            else
            {
                // 释放当前持有的方块
                ReleaseBlock();
            }
            
            // 提供触觉反馈
            PulseHapticFeedback(hapticSettings.GrabPulseDuration, 
                              hapticSettings.GrabPulseStrength);
        }
        
        /// <summary>
        /// 尝试抓取方块
        /// </summary>
        private void TryGrabBlock()
        {
            // 使用物理射线检测
            RaycastHit hit;
            if (Physics.Raycast(transform.position, transform.forward, out hit, 2.0f))
            {
                BlockController block = hit.collider.GetComponent<BlockController>();
                if (block != null && block.CanBeGrabbed)
                {
                    // 抓取方块
                    GrabBlock(block);
                }
            }
        }
        
        /// <summary>
        /// 抓取方块
        /// </summary>
        private void GrabBlock(BlockController block)
        {
            heldBlock = block;
            isHoldingBlock = true;
            
            // 设置方块为被抓取状态
            heldBlock.OnGrabbed(transform);
            
            // 激活激光指针进行精确放置
            SetLaserPointerActive(true);
            
            // 播放抓取音效
            AudioManager.Instance.PlaySpatialSound("block_grab", 
                transform.position, 
                1.0f);
        }
        
        /// <summary>
        /// 释放方块
        /// </summary>
        private void ReleaseBlock()
        {
            if (heldBlock != null)
            {
                // 检查放置位置是否有效
                bool isValidPlacement = CheckPlacementValidity();
                
                if (isValidPlacement)
                {
                    // 放置方块
                    heldBlock.OnReleased(true);
                    
                    // 播放放置音效
                    AudioManager.Instance.PlaySpatialSound("block_place", 
                        heldBlock.transform.position, 
                        1.0f);
                }
                else
                {
                    // 返回原始位置
                    heldBlock.OnReleased(false);
                    
                    // 播放错误音效
                    AudioManager.Instance.PlaySpatialSound("error", 
                        transform.position, 
                        0.8f);
                    
                    // 提供错误反馈
                    PulseHapticFeedback(hapticSettings.ErrorPulseDuration, 
                                      hapticSettings.ErrorPulseStrength);
                }
                
                heldBlock = null;
                isHoldingBlock = false;
                
                // 关闭激光指针
                SetLaserPointerActive(false);
            }
        }
        
        /// <summary>
        /// 检查放置位置有效性
        /// </summary>
        private bool CheckPlacementValidity()
        {
            if (heldBlock == null)
            {
                return false;
            }
            
            // 商业项目中使用网格系统验证
            GridSystem grid = GridSystem.Instance;
            if (grid != null)
            {
                return grid.IsValidPlacement(heldBlock);
            }
            
            return false;
        }
        
        /// <summary>
        /// 设置激光指针激活状态
        /// </summary>
        private void SetLaserPointerActive(bool active)
        {
            isLaserActive = active;
            laserPointer.enabled = active;
        }
        
        /// <summary>
        /// 提供触觉反馈脉冲
        /// </summary>
        private void PulseHapticFeedback(float duration, float strength)
        {
            StartCoroutine(HapticPulseCoroutine(duration, strength));
        }
        
        /// <summary>
        /// 触觉脉冲协程
        /// </summary>
        private IEnumerator HapticPulseCoroutine(float duration, float strength)
        {
            if (controllerPose != null)
            {
                SteamVR_Input_Sources source = controllerPose.inputSource;
                
                // 商业项目中通常使用更复杂的脉冲模式
                for (float t = 0; t < duration; t += Time.deltaTime)
                {
                    float pulseStrength = Mathf.Sin(t * Mathf.PI * 4) * strength;
                    SteamVR_Actions.default_Haptic[source].Execute(0, 
                        Time.deltaTime, 
                        100, 
                        pulseStrength);
                    yield return null;
                }
            }
        }
        
        /// <summary>
        /// 每帧更新
        /// </summary>
        private void Update()
        {
            if (isLaserActive)
            {
                UpdateLaserPointer();
            }
            
            // 处理触摸板输入
            HandleTouchpadInput();
        }
        
        /// <summary>
        /// 更新激光指针
        /// </summary>
        private void UpdateLaserPointer()
        {
            RaycastHit hit;
            if (Physics.Raycast(transform.position, transform.forward, out hit, 5.0f))
            {
                // 更新激光指针终点
                laserPointer.SetPosition(0, transform.position);
                laserPointer.SetPosition(1, hit.point);
                
                // 显示放置预览
                if (heldBlock != null)
                {
                    heldBlock.ShowPlacementPreview(hit.point, hit.normal);
                }
            }
            else
            {
                // 没有命中时显示最大距离
                Vector3 endPoint = transform.position + transform.forward * 5.0f;
                laserPointer.SetPosition(0, transform.position);
                laserPointer.SetPosition(1, endPoint);
            }
        }
        
        /// <summary>
        /// 处理触摸板输入
        /// </summary>
        private void HandleTouchpadInput()
        {
            if (touchpadAction != null && isHoldingBlock)
            {
                Vector2 touchpadValue = touchpadAction.GetAxis(inputSource);
                
                if (touchpadValue.magnitude > 0.1f)
                {
                    // 根据触摸板输入旋转方块
                    RotateHeldBlock(touchpadValue);
                }
            }
        }
        
        /// <summary>
        /// 旋转持有的方块
        /// </summary>
        private void RotateHeldBlock(Vector2 input)
        {
            if (heldBlock != null)
            {
                // 计算旋转轴和角度
                Vector3 rotationAxis = new Vector3(input.y, 0, -input.x);
                float rotationAmount = 90.0f; // 每次旋转90度
                
                heldBlock.RotateBlock(rotationAxis, rotationAmount);
                
                // 提供旋转反馈
                PulseHapticFeedback(0.1f, 0.3f);
                AudioManager.Instance.PlaySpatialSound("block_rotate", 
                    heldBlock.transform.position, 
                    0.7f);
            }
        }
    }
    
    /// <summary>
    /// 触觉反馈设置
    /// </summary>
    [System.Serializable]
    public class HapticFeedbackSettings
    {
        public float GrabPulseDuration = 0.1f;
        public float GrabPulseStrength = 0.5f;
        public float RotatePulseDuration = 0.05f;
        public float RotatePulseStrength = 0.3f;
        public float ErrorPulseDuration = 0.2f;
        public float ErrorPulseStrength = 0.8f;
    }
}

11.2 项目规划与资源预处理

11.2.1 商业VR项目开发方法论

在商业VR游戏开发中,采用敏捷开发与瀑布模型结合的混合方法论。俄罗斯方块VR项目的开发周期通常为4-6个月,分为预生产、原型开发、Alpha、Beta和发布阶段。每个阶段都有明确的交付物和质量标准。

预生产阶段的关键任务包括:

  1. 技术可行性验证:确保HTC VIVE硬件能够支持设计的功能
  2. 性能目标设定:目标帧率90FPS,渲染延迟低于20ms
  3. 美术风格确定:采用低多边形风格以确保性能,同时保持视觉吸引力
  4. 交互设计验证:通过纸面原型和简单VR原型测试核心交互

资源管理策略需要特别关注VR环境的高性能要求。所有3D模型必须遵循以下规范:

  • 单个方块模型面数不超过500个三角形
  • 使用单一材质球,最多2个纹理贴图(漫反射+法线)
  • 纹理尺寸不超过1024x1024
  • 避免使用实时阴影,采用光照贴图
using UnityEngine;
using System.Collections.Generic;
using System.IO;

namespace VR.Tetris.AssetManagement
{
    /// <summary>
    /// VR资源管理器
    /// 负责加载、管理和优化游戏资源
    /// </summary>
    public class VRResourceManager : MonoBehaviour
    {
        // 资源缓存
        private Dictionary<string, GameObject> blockPrefabCache = 
            new Dictionary<string, GameObject>();
        private Dictionary<string, Material> materialCache = 
            new Dictionary<string, Material>();
        private Dictionary<string, AudioClip> audioClipCache = 
            new Dictionary<string, AudioClip>();
        
        // 资源路径配置
        [SerializeField]
        private ResourcePathConfiguration pathConfig;
        
        // 性能监控
        private ResourcePerformanceMonitor performanceMonitor;
        
        /// <summary>
        /// 异步加载方块预制体
        /// </summary>
        public IEnumerator LoadBlockPrefabAsync(string blockId, 
            System.Action<GameObject> onComplete)
        {
            // 检查缓存
            if (blockPrefabCache.ContainsKey(blockId))
            {
                onComplete?.Invoke(blockPrefabCache[blockId]);
                yield break;
            }
            
            // 构建资源路径
            string resourcePath = Path.Combine(pathConfig.BlockPrefabPath, blockId);
            
            // 开始性能监控
            performanceMonitor?.StartLoadOperation($"Block_{blockId}");
            
            // 异步加载
            ResourceRequest request = Resources.LoadAsync<GameObject>(resourcePath);
            yield return request;
            
            if (request.asset != null)
            {
                GameObject prefab = request.asset as GameObject;
                
                // 验证资源是否符合VR性能规范
                if (ValidateBlockPrefab(prefab))
                {
                    // 缓存资源
                    blockPrefabCache[blockId] = prefab;
                    
                    // 记录加载完成
                    performanceMonitor?.EndLoadOperation($"Block_{blockId}", true);
                    
                    onComplete?.Invoke(prefab);
                }
                else
                {
                    Debug.LogWarning($"方块预制体 {blockId} 不符合性能规范");
                    performanceMonitor?.EndLoadOperation($"Block_{blockId}", false);
                    onComplete?.Invoke(null);
                }
            }
            else
            {
                Debug.LogError($"无法加载方块预制体: {blockId}");
                performanceMonitor?.EndLoadOperation($"Block_{blockId}", false);
                onComplete?.Invoke(null);
            }
        }
        
        /// <summary>
        /// 验证方块预制体性能
        /// </summary>
        private bool ValidateBlockPrefab(GameObject prefab)
        {
            if (prefab == null)
            {
                return false;
            }
            
            // 检查网格复杂度
            MeshFilter meshFilter = prefab.GetComponentInChildren<MeshFilter>();
            if (meshFilter != null && meshFilter.sharedMesh != null)
            {
                int triangleCount = meshFilter.sharedMesh.triangles.Length / 3;
                if (triangleCount > pathConfig.MaxTrianglesPerBlock)
                {
                    Debug.LogWarning($"方块面数过多: {triangleCount} > {pathConfig.MaxTrianglesPerBlock}");
                    return false;
                }
            }
            
            // 检查材质数量
            Renderer[] renderers = prefab.GetComponentsInChildren<Renderer>();
            foreach (Renderer renderer in renderers)
            {
                if (renderer.sharedMaterials.Length > pathConfig.MaxMaterialsPerBlock)
                {
                    Debug.LogWarning($"方块材质数量过多: {renderer.sharedMaterials.Length}");
                    return false;
                }
                
                // 检查纹理尺寸
                foreach (Material material in renderer.sharedMaterials)
                {
                    if (material.mainTexture != null)
                    {
                        int maxSize = Mathf.Max(material.mainTexture.width, 
                            material.mainTexture.height);
                        if (maxSize > pathConfig.MaxTextureSize)
                        {
                            Debug.LogWarning($"纹理尺寸过大: {maxSize} > {pathConfig.MaxTextureSize}");
                            return false;
                        }
                    }
                }
            }
            
            // 检查碰撞器
            Collider[] colliders = prefab.GetComponentsInChildren<Collider>();
            if (colliders.Length == 0)
            {
                Debug.LogWarning("方块缺少碰撞器");
                return false;
            }
            
            return true;
        }
        
        /// <summary>
        /// 预加载关键资源
        /// 商业项目中通常在加载界面调用
        /// </summary>
        public IEnumerator PreloadCriticalResources(System.Action<float> onProgress)
        {
            List<string> criticalResources = new List<string>
            {
                "Block_I",
                "Block_J",
                "Block_L",
                "Block_O",
                "Block_S",
                "Block_T",
                "Block_Z"
            };
            
            int loadedCount = 0;
            int totalCount = criticalResources.Count;
            
            foreach (string resourceId in criticalResources)
            {
                yield return LoadBlockPrefabAsync(resourceId, (prefab) =>
                {
                    loadedCount++;
                    float progress = (float)loadedCount / totalCount;
                    onProgress?.Invoke(progress);
                });
            }
            
            // 预加载音频资源
            yield return PreloadAudioResources();
            
            Debug.Log($"关键资源预加载完成,共加载{loadedCount}个资源");
        }
        
        /// <summary>
        /// 预加载音频资源
        /// </summary>
        private IEnumerator PreloadAudioResources()
        {
            string[] audioClips = {
                "block_grab",
                "block_place",
                "block_rotate",
                "line_clear",
                "tetris_clear",
                "error",
                "game_over"
            };
            
            foreach (string clipName in audioClips)
            {
                string resourcePath = Path.Combine(pathConfig.AudioClipPath, clipName);
                ResourceRequest request = Resources.LoadAsync<AudioClip>(resourcePath);
                yield return request;
                
                if (request.asset != null)
                {
                    audioClipCache[clipName] = request.asset as AudioClip;
                }
            }
        }
        
        /// <summary>
        /// 清理未使用资源
        /// 商业项目中在场景切换时调用
        /// </summary>
        public void CleanupUnusedResources()
        {
            // 清理长时间未使用的资源
            List<string> resourcesToRemove = new List<string>();
            
            foreach (var kvp in blockPrefabCache)
            {
                // 如果资源没有被引用,则标记为可清理
                if (kvp.Value != null && kvp.Value.GetInstanceID() < 0)
                {
                    resourcesToRemove.Add(kvp.Key);
                }
            }
            
            foreach (string key in resourcesToRemove)
            {
                blockPrefabCache.Remove(key);
            }
            
            // 触发垃圾回收
            Resources.UnloadUnusedAssets();
            System.GC.Collect();
        }
        
        /// <summary>
        /// 获取资源使用统计
        /// </summary>
        public ResourceUsageStats GetResourceUsageStats()
        {
            ResourceUsageStats stats = new ResourceUsageStats
            {
                BlockPrefabCount = blockPrefabCache.Count,
                MaterialCount = materialCache.Count,
                AudioClipCount = audioClipCache.Count,
                TotalMemoryUsage = CalculateTotalMemoryUsage()
            };
            
            return stats;
        }
        
        /// <summary>
        /// 计算总内存使用量
        /// </summary>
        private long CalculateTotalMemoryUsage()
        {
            long totalBytes = 0;
            
            // 这里简化计算,实际项目中需要使用Profiler API
            foreach (var kvp in blockPrefabCache)
            {
                if (kvp.Value != null)
                {
                    // 估算内存使用
                    totalBytes += 1024 * 100; // 假设每个预制体约100KB
                }
            }
            
            return totalBytes;
        }
    }
    
    /// <summary>
    /// 资源路径配置
    /// </summary>
    [System.Serializable]
    public class ResourcePathConfiguration
    {
        public string BlockPrefabPath = "Prefabs/Blocks";
        public string AudioClipPath = "Audio/GameSFX";
        public string MaterialPath = "Materials/BlockMaterials";
        
        // 性能限制
        public int MaxTrianglesPerBlock = 500;
        public int MaxMaterialsPerBlock = 2;
        public int MaxTextureSize = 1024;
    }
    
    /// <summary>
    /// 资源使用统计
    /// </summary>
    public struct ResourceUsageStats
    {
        public int BlockPrefabCount;
        public int MaterialCount;
        public int AudioClipCount;
        public long TotalMemoryUsage;
        
        public override string ToString()
        {
            return $"资源使用统计: " +
                   $"方块预制体={BlockPrefabCount}, " +
                   $"材质={MaterialCount}, " +
                   $"音频片段={AudioClipCount}, " +
                   $"内存使用={TotalMemoryUsage / 1024}KB";
        }
    }
    
    /// <summary>
    /// 资源性能监控器
    /// </summary>
    public class ResourcePerformanceMonitor
    {
        private Dictionary<string, LoadOperationInfo> loadOperations = 
            new Dictionary<string, LoadOperationInfo>();
        
        public void StartLoadOperation(string operationId)
        {
            loadOperations[operationId] = new LoadOperationInfo
            {
                StartTime = Time.realtimeSinceStartup,
                OperationId = operationId
            };
        }
        
        public void EndLoadOperation(string operationId, bool success)
        {
            if (loadOperations.ContainsKey(operationId))
            {
                LoadOperationInfo info = loadOperations[operationId];
                info.EndTime = Time.realtimeSinceStartup;
                info.Duration = info.EndTime - info.StartTime;
                info.Success = success;
                
                // 记录到分析系统
                LogLoadOperation(info);
                
                loadOperations.Remove(operationId);
            }
        }
        
        private void LogLoadOperation(LoadOperationInfo info)
        {
            // 商业项目中记录到分析服务
            Debug.Log($"资源加载操作: {info.OperationId}, " +
                     $"耗时: {info.Duration:F2}秒, " +
                     $"状态: {(info.Success ? "成功" : "失败")}");
        }
        
        private class LoadOperationInfo
        {
            public string OperationId;
            public float StartTime;
            public float EndTime;
            public float Duration;
            public bool Success;
        }
    }
}

11.2.2 Unity项目配置与优化设置

在Unity 2021.3.8f1c1中配置VR项目需要特别注意渲染管线的选择和项目设置优化。对于HTC VIVE开发,推荐使用Unity的通用渲染管线(URP),它提供了良好的性能平衡和VR支持。

项目设置的关键配置包括:

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

namespace VR.Tetris.Configuration
{
    /// <summary>
    /// Unity项目配置管理器
    /// 负责设置和验证项目配置
    /// </summary>
    public class ProjectConfigurationManager : MonoBehaviour
    {
        [SerializeField]
        private VRProjectSettings projectSettings;
        
        /// <summary>
        /// 初始化项目配置
        /// 在游戏启动时调用
        /// </summary>
        public void InitializeProjectConfiguration()
        {
            // 验证Unity版本
            ValidateUnityVersion();
            
            // 配置图形设置
            ConfigureGraphicsSettings();
            
            // 配置输入设置
            ConfigureInputSettings();
            
            // 配置音频设置
            ConfigureAudioSettings();
            
            // 配置物理设置
            ConfigurePhysicsSettings();
            
            // 应用VR特定设置
            ApplyVRSpecificSettings();
            
            // 验证配置
            ValidateConfiguration();
            
            Debug.Log("项目配置初始化完成");
        }
        
        /// <summary>
        /// 验证Unity版本
        /// </summary>
        private void ValidateUnityVersion()
        {
            string currentVersion = Application.unityVersion;
            string requiredVersion = "2021.3.8f1";
            
            if (!currentVersion.StartsWith("2021.3"))
            {
                Debug.LogWarning($"项目为Unity {requiredVersion}设计,当前版本: {currentVersion}");
                Debug.LogWarning("某些功能可能无法正常工作");
            }
        }
        
        /// <summary>
        /// 配置图形设置
        /// </summary>
        private void ConfigureGraphicsSettings()
        {
            // 设置目标帧率
            Application.targetFrameRate = projectSettings.TargetFrameRate;
            QualitySettings.vSyncCount = 0; // VR中禁用垂直同步
            
            // 配置抗锯齿
            if (projectSettings.EnableMSAA)
            {
                QualitySettings.antiAliasing = 4; // 4x MSAA
            }
            else
            {
                QualitySettings.antiAliasing = 0;
            }
            
            // 配置阴影
            QualitySettings.shadows = projectSettings.EnableShadows ? 
                ShadowQuality.All : ShadowQuality.Disable;
            QualitySettings.shadowResolution = ShadowResolution.Low;
            QualitySettings.shadowDistance = 10.0f;
            
            // 配置纹理质量
            QualitySettings.masterTextureLimit = projectSettings.TextureQuality;
            
            // 配置URP设置
            ConfigureURPSettings();
        }
        
        /// <summary>
        /// 配置URP设置
        /// </summary>
        private void ConfigureURPSettings()
        {
            UniversalRenderPipelineAsset urpAsset = 
                GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
            
            if (urpAsset != null)
            {
                // VR特定优化
                urpAsset.supportsCameraDepthTexture = true;
                urpAsset.supportsCameraOpaqueTexture = false; // 节省内存
                urpAsset.msaaSampleCount = projectSettings.EnableMSAA ? 4 : 1;
                
                // 配置渲染比例
                urpAsset.renderScale = projectSettings.RenderScale;
                
                // 启用XR设置
                urpAsset.supportsStereo = true;
                urpAsset.xrRendering = true;
                
                Debug.Log("URP配置已更新");
            }
            else
            {
                Debug.LogError("未找到URP资产,请确保项目使用通用渲染管线");
            }
        }
        
        /// <summary>
        /// 配置输入设置
        /// </summary>
        private void ConfigureInputSettings()
        {
            // 启用XR输入
            UnityEngine.XR.XRSettings.enabled = true;
            
            // 配置SteamVR输入(如果使用)
            if (projectSettings.EnableSteamVRInput)
            {
                ConfigureSteamVRInput();
            }
        }
        
        /// <summary>
        /// 配置SteamVR输入
        /// </summary>
        private void ConfigureSteamVRInput()
        {
            // 检查SteamVR插件是否已导入
            bool steamVRAvailable = false;
            
            #if STEAMVR_INSTALLED
            steamVRAvailable = true;
            
            // 初始化SteamVR输入
            SteamVR.Initialize();
            
            // 设置动作清单路径
            SteamVR_Settings.instance.activateFirstActionSetOnStart = false;
            SteamVR_Settings.instance.inputFilePath = "actions.json";
            
            Debug.Log("SteamVR输入已配置");
            #endif
            
            if (!steamVRAvailable)
            {
                Debug.LogWarning("SteamVR插件未安装,将使用Unity XR输入");
            }
        }
        
        /// <summary>
        /// 配置音频设置
        /// </summary>
        private void ConfigureAudioSettings()
        {
            // 启用空间音频
            AudioSettings.speakerMode = AudioSpeakerMode.Mode7point1;
            AudioSettings.dspBufferSize = 512; // 低延迟设置
            
            // 配置混音器
            if (projectSettings.MasterMixer != null)
            {
                AudioListener.volume = 1.0f;
                
                // 设置混音器参数
                projectSettings.MasterMixer.SetFloat("MasterVolume", 0.0f);
                projectSettings.MasterMixer.SetFloat("SFXVolume", 0.0f);
                projectSettings.MasterMixer.SetFloat("MusicVolume", -5.0f);
            }
        }
        
        /// <summary>
        /// 配置物理设置
        /// </summary>
        private void ConfigurePhysicsSettings()
        {
            // 优化物理设置
            Physics.defaultSolverIterations = 4;
            Physics.defaultSolverVelocityIterations = 1;
            Physics.reuseCollisionCallbacks = true;
            
            // 配置层碰撞矩阵
            ConfigureLayerCollisionMatrix();
        }
        
        /// <summary>
        /// 配置层碰撞矩阵
        /// </summary>
        private void ConfigureLayerCollisionMatrix()
        {
            // 定义图层
            int blockLayer = LayerMask.NameToLayer("Block");
            int playerLayer = LayerMask.NameToLayer("Player");
            int uiLayer = LayerMask.NameToLayer("UI");
            int environmentLayer = LayerMask.NameToLayer("Environment");
            
            // 配置碰撞关系
            Physics.IgnoreLayerCollision(blockLayer, uiLayer, true);
            Physics.IgnoreLayerCollision(playerLayer, uiLayer, true);
            Physics.IgnoreLayerCollision(blockLayer, playerLayer, false);
            Physics.IgnoreLayerCollision(blockLayer, environmentLayer, false);
        }
        
        /// <summary>
        /// 应用VR特定设置
        /// </summary>
        private void ApplyVRSpecificSettings()
        {
            // 设置XR设备
            UnityEngine.XR.XRSettings.LoadDeviceByName(projectSettings.XRDeviceName);
            
            // 等待一帧让XR设备初始化
            StartCoroutine(EnableXRDevice());
            
            // 配置XR呈现
            UnityEngine.XR.XRSettings.renderViewportScale = projectSettings.RenderViewportScale;
            UnityEngine.XR.XRSettings.eyeTextureResolutionScale = projectSettings.EyeTextureResolutionScale;
            
            // 启用跟踪
            UnityEngine.XR.InputTracking.disablePositionalTracking = false;
        }
        
        private System.Collections.IEnumerator EnableXRDevice()
        {
            yield return null; // 等待一帧
            
            UnityEngine.XR.XRSettings.enabled = true;
            
            // 验证XR设备
            if (UnityEngine.XR.XRDevice.isPresent)
            {
                Debug.Log($"XR设备已启用: {UnityEngine.XR.XRSettings.loadedDeviceName}");
            }
            else
            {
                Debug.LogWarning("未检测到XR设备,将在非VR模式下运行");
            }
        }
        
        /// <summary>
        /// 验证配置
        /// </summary>
        private void ValidateConfiguration()
        {
            List<string> warnings = new List<string>();
            List<string> errors = new List<string>();
            
            // 检查帧率设置
            if (Application.targetFrameRate < 90 && UnityEngine.XR.XRDevice.isPresent)
            {
                warnings.Add("VR模式下建议目标帧率设置为90");
            }
            
            // 检查渲染比例
            UniversalRenderPipelineAsset urpAsset = 
                GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
            if (urpAsset != null && urpAsset.renderScale > 1.5f)
            {
                warnings.Add("渲染比例过高可能影响性能");
            }
            
            // 检查音频设置
            if (AudioSettings.GetConfiguration().sampleRate < 44100)
            {
                warnings.Add("音频采样率低于44.1kHz可能影响音质");
            }
            
            // 输出验证结果
            if (warnings.Count > 0)
            {
                Debug.LogWarning("配置验证警告:");
                foreach (string warning in warnings)
                {
                    Debug.LogWarning($"  - {warning}");
                }
            }
            
            if (errors.Count > 0)
            {
                Debug.LogError("配置验证错误:");
                foreach (string error in errors)
                {
                    Debug.LogError($"  - {error}");
                }
            }
            else
            {
                Debug.Log("配置验证通过");
            }
        }
        
        /// <summary>
        /// 获取性能报告
        /// </summary>
        public PerformanceReport GetPerformanceReport()
        {
            PerformanceReport report = new PerformanceReport
            {
                FrameRate = 1.0f / Time.deltaTime,
                UsedMemory = System.GC.GetTotalMemory(false) / (1024 * 1024),
                DrawCallCount = UnityEditor.UnityStats.drawCalls,
                TriangleCount = UnityEditor.UnityStats.triangles,
                VRDevicePresent = UnityEngine.XR.XRDevice.isPresent,
                XRDeviceName = UnityEngine.XR.XRSettings.loadedDeviceName
            };
            
            return report;
        }
    }
    
    /// <summary>
    /// VR项目设置
    /// </summary>
    [System.Serializable]
    public class VRProjectSettings
    {
        [Header("图形设置")]
        public int TargetFrameRate = 90;
        public bool EnableMSAA = true;
        public bool EnableShadows = true;
        public int TextureQuality = 1; // 0=全分辨率, 1=半分辨率, 2=四分之一分辨率
        public float RenderScale = 1.2f;
        
        [Header("VR设置")]
        public string XRDeviceName = "OpenVR";
        public float RenderViewportScale = 1.0f;
        public float EyeTextureResolutionScale = 1.5f;
        
        [Header("输入设置")]
        public bool EnableSteamVRInput = true;
        
        [Header("音频设置")]
        public AudioMixer MasterMixer;
    }
    
    /// <summary>
    /// 性能报告
    /// </summary>
    public struct PerformanceReport
    {
        public float FrameRate;
        public long UsedMemory; // MB
        public int DrawCallCount;
        public int TriangleCount;
        public bool VRDevicePresent;
        public string XRDeviceName;
        
        public override string ToString()
        {
            return $"性能报告: " +
                   $"帧率={FrameRate:F1}FPS, " +
                   $"内存={UsedMemory}MB, " +
                   $"绘制调用={DrawCallCount}, " +
                   $"三角形数量={TriangleCount}, " +
                   $"VR设备={(VRDevicePresent ? "已连接" : "未连接")}, " +
                   $"设备名称={XRDeviceName}";
        }
    }
}

由于篇幅限制,本文展示了前两个主要章节的详细内容,涵盖了VR俄罗斯方块项目的设计理念、交互机制、项目规划和Unity配置。在实际商业项目中,还需要继续开发场景管理、游戏逻辑、UI系统、排行榜功能以及性能优化等模块。每个模块都需要遵循相同的代码规范和设计原则,确保项目的可维护性和性能表现。

在后续开发中,需要特别注意VR特有的挑战,如运动舒适度、空间定位精度、性能优化等,同时保持游戏的核心玩法乐趣,为玩家提供沉浸式的俄罗斯方块体验。

第11章 基于HTC VIVE的沉浸式俄罗斯方块开发实践(续)

11.3 游戏系统架构与模块设计

11.3.1 三维游戏场景构建策略

在商业VR项目中,场景构建不仅涉及视觉表现,更关乎性能优化和用户体验。俄罗斯方块的VR化需要将传统的二维网格系统扩展到三维空间,同时保持游戏的核心逻辑不变。我们采用了分层网格系统,其中每一层代表传统俄罗斯方块中的一个"行",而三维扩展则通过深度维度实现。

场景构建的关键技术包括:

  1. 动态网格生成:根据玩家位置和游戏区域大小实时生成网格系统
  2. 空间分区:使用八叉树(Octree)技术优化碰撞检测和渲染
  3. LOD系统:根据方块与玩家的距离动态调整渲染细节
  4. 实例化渲染:相同类型的方块使用GPU实例化技术减少绘制调用
using UnityEngine;
using System.Collections.Generic;
using System.Linq;

namespace VR.Tetris.Core
{
    /// <summary>
    /// 三维网格系统
    /// 管理游戏区域的方块放置和碰撞检测
    /// </summary>
    public class Grid3DSystem : MonoBehaviour
    {
        // 网格配置
        [SerializeField]
        private GridConfiguration gridConfig;
        
        // 网格数据结构
        private Cell[,,] gridCells;
        private Octree<Cell> spatialPartition;
        
        // 视觉表示
        private GameObject gridVisual;
        private Material gridMaterial;
        
        // 性能优化
        private List<Cell> dirtyCells = new List<Cell>();
        private float lastUpdateTime = 0f;
        private const float UPDATE_INTERVAL = 0.1f;
        
        /// <summary>
        /// 初始化三维网格
        /// </summary>
        public void InitializeGrid()
        {
            // 计算网格尺寸
            int width = Mathf.CeilToInt(gridConfig.GridSize.x / gridConfig.CellSize);
            int height = Mathf.CeilToInt(gridConfig.GridSize.y / gridConfig.CellSize);
            int depth = Mathf.CeilToInt(gridConfig.GridSize.z / gridConfig.CellSize);
            
            // 创建网格数组
            gridCells = new Cell[width, height, depth];
            
            // 初始化空间分区
            Bounds gridBounds = new Bounds(
                gridConfig.GridCenter,
                gridConfig.GridSize
            );
            spatialPartition = new Octree<Cell>(gridBounds, gridConfig.MinNodeSize);
            
            // 初始化所有单元格
            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    for (int z = 0; z < depth; z++)
                    {
                        Vector3 cellPosition = GetCellWorldPosition(x, y, z);
                        Cell cell = new Cell(x, y, z, cellPosition);
                        gridCells[x, y, z] = cell;
                        
                        // 添加到空间分区
                        spatialPartition.Add(cell, cell.Bounds);
                    }
                }
            }
            
            // 创建网格可视化
            CreateGridVisualization();
            
            Debug.Log($"三维网格初始化完成: {width}x{height}x{depth}");
        }
        
        /// <summary>
        /// 获取单元格世界坐标
        /// </summary>
        private Vector3 GetCellWorldPosition(int x, int y, int z)
        {
            Vector3 startPos = gridConfig.GridCenter - gridConfig.GridSize * 0.5f;
            startPos += new Vector3(
                x * gridConfig.CellSize + gridConfig.CellSize * 0.5f,
                y * gridConfig.CellSize + gridConfig.CellSize * 0.5f,
                z * gridConfig.CellSize + gridConfig.CellSize * 0.5f
            );
            return startPos;
        }
        
        /// <summary>
        /// 检查方块是否可以放置在指定位置
        /// </summary>
        public PlacementResult CanPlaceBlock(BlockController block, Vector3 position, Quaternion rotation)
        {
            if (block == null)
            {
                return PlacementResult.Invalid;
            }
            
            // 获取方块占据的单元格
            List<CellOccupancy> occupiedCells = block.GetOccupiedCells(position, rotation);
            
            PlacementResult result = new PlacementResult
            {
                IsValid = true,
                OccupiedCells = new List<Cell>()
            };
            
            // 检查每个被占用的单元格
            foreach (CellOccupancy occupancy in occupiedCells)
            {
                // 转换为网格坐标
                Vector3Int gridCoords = WorldToGridCoordinates(occupancy.Position);
                
                // 检查边界
                if (!IsWithinGridBounds(gridCoords))
                {
                    result.IsValid = false;
                    result.InvalidReason = "超出网格边界";
                    return result;
                }
                
                // 检查单元格是否已被占用
                Cell cell = gridCells[gridCoords.x, gridCoords.y, gridCoords.z];
                if (cell.IsOccupied && cell.OccupyingBlock != block)
                {
                    result.IsValid = false;
                    result.InvalidReason = "单元格已被占用";
                    result.ConflictingCell = cell;
                    return result;
                }
                
                // 检查支撑条件
                if (gridConfig.RequireBottomSupport && !CheckBottomSupport(gridCoords))
                {
                    result.IsValid = false;
                    result.InvalidReason = "缺乏底部支撑";
                    return result;
                }
                
                result.OccupiedCells.Add(cell);
            }
            
            // 检查特殊规则(如悬空检测)
            if (gridConfig.EnableOverhangDetection)
            {
                if (HasUnsupportedOverhang(occupiedCells))
                {
                    result.IsValid = false;
                    result.InvalidReason = "存在未支撑的悬空部分";
                    return result;
                }
            }
            
            return result;
        }
        
        /// <summary>
        /// 放置方块到网格
        /// </summary>
        public bool PlaceBlock(BlockController block, Vector3 position, Quaternion rotation)
        {
            PlacementResult placementCheck = CanPlaceBlock(block, position, rotation);
            
            if (!placementCheck.IsValid)
            {
                Debug.LogWarning($"无法放置方块: {placementCheck.InvalidReason}");
                return false;
            }
            
            // 更新单元格状态
            foreach (Cell cell in placementCheck.OccupiedCells)
            {
                cell.OccupyingBlock = block;
                cell.IsOccupied = true;
                cell.LastUpdateTime = Time.time;
                
                // 标记为脏单元格(需要更新可视化)
                MarkCellDirty(cell);
                
                // 更新空间分区
                spatialPartition.Update(cell, cell.Bounds);
            }
            
            // 检查并清除完整层
            CheckForCompleteLayers();
            
            // 更新网格可视化
            UpdateGridVisualization();
            
            return true;
        }
        
        /// <summary>
        /// 检查并清除完整层
        /// </summary>
        private void CheckForCompleteLayers()
        {
            List<int> completedLayers = new List<int>();
            
            // 检查每一层(Y轴)
            for (int y = 0; y < gridCells.GetLength(1); y++)
            {
                if (IsLayerComplete(y))
                {
                    completedLayers.Add(y);
                }
            }
            
            // 清除完整层
            foreach (int layerY in completedLayers.OrderByDescending(y => y))
            {
                ClearLayer(layerY);
                ShiftLayersDown(layerY);
                
                // 触发得分事件
                OnLayerCleared?.Invoke(layerY, completedLayers.Count);
            }
            
            // 如果有多个层同时被清除(俄罗斯方块)
            if (completedLayers.Count >= 4)
            {
                OnTetrisCleared?.Invoke(completedLayers.Count);
            }
        }
        
        /// <summary>
        /// 检查层是否完整
        /// </summary>
        private bool IsLayerComplete(int layerY)
        {
            for (int x = 0; x < gridCells.GetLength(0); x++)
            {
                for (int z = 0; z < gridCells.GetLength(2); z++)
                {
                    if (!gridCells[x, layerY, z].IsOccupied)
                    {
                        return false;
                    }
                }
            }
            return true;
        }
        
        /// <summary>
        /// 清除指定层
        /// </summary>
        private void ClearLayer(int layerY)
        {
            for (int x = 0; x < gridCells.GetLength(0); x++)
            {
                for (int z = 0; z < gridCells.GetLength(2); z++)
                {
                    Cell cell = gridCells[x, layerY, z];
                    if (cell.OccupyingBlock != null)
                    {
                        // 通知方块被清除
                        cell.OccupyingBlock.OnClearedFromGrid();
                    }
                    
                    cell.OccupyingBlock = null;
                    cell.IsOccupied = false;
                    MarkCellDirty(cell);
                }
            }
        }
        
        /// <summary>
        /// 将上层向下移动
        /// </summary>
        private void ShiftLayersDown(int clearedLayerY)
        {
            for (int y = clearedLayerY + 1; y < gridCells.GetLength(1); y++)
            {
                for (int x = 0; x < gridCells.GetLength(0); x++)
                {
                    for (int z = 0; z < gridCells.GetLength(2); z++)
                    {
                        Cell currentCell = gridCells[x, y, z];
                        Cell cellBelow = gridCells[x, y - 1, z];
                        
                        if (currentCell.IsOccupied && currentCell.OccupyingBlock != null)
                        {
                            // 移动方块到下层
                            cellBelow.OccupyingBlock = currentCell.OccupyingBlock;
                            cellBelow.IsOccupied = true;
                            
                            // 更新方块位置
                            Vector3 newPosition = GetCellWorldPosition(x, y - 1, z);
                            cellBelow.OccupyingBlock.MoveTo(newPosition);
                            
                            // 清除原单元格
                            currentCell.OccupyingBlock = null;
                            currentCell.IsOccupied = false;
                            
                            // 标记单元格更新
                            MarkCellDirty(currentCell);
                            MarkCellDirty(cellBelow);
                        }
                    }
                }
            }
        }
        
        /// <summary>
        /// 世界坐标转换为网格坐标
        /// </summary>
        private Vector3Int WorldToGridCoordinates(Vector3 worldPosition)
        {
            Vector3 localPos = worldPosition - (gridConfig.GridCenter - gridConfig.GridSize * 0.5f);
            
            int x = Mathf.FloorToInt(localPos.x / gridConfig.CellSize);
            int y = Mathf.FloorToInt(localPos.y / gridConfig.CellSize);
            int z = Mathf.FloorToInt(localPos.z / gridConfig.CellSize);
            
            return new Vector3Int(x, y, z);
        }
        
        /// <summary>
        /// 检查是否在网格边界内
        /// </summary>
        private bool IsWithinGridBounds(Vector3Int gridCoords)
        {
            return gridCoords.x >= 0 && gridCoords.x < gridCells.GetLength(0) &&
                   gridCoords.y >= 0 && gridCoords.y < gridCells.GetLength(1) &&
                   gridCoords.z >= 0 && gridCoords.z < gridCells.GetLength(2);
        }
        
        /// <summary>
        /// 检查底部支撑
        /// </summary>
        private bool CheckBottomSupport(Vector3Int gridCoords)
        {
            // 如果是最底层,始终有支撑
            if (gridCoords.y == 0)
            {
                return true;
            }
            
            // 检查正下方的单元格
            Vector3Int belowCoords = new Vector3Int(
                gridCoords.x,
                gridCoords.y - 1,
                gridCoords.z
            );
            
            if (IsWithinGridBounds(belowCoords))
            {
                return gridCells[belowCoords.x, belowCoords.y, belowCoords.z].IsOccupied;
            }
            
            return false;
        }
        
        /// <summary>
        /// 检查未支撑的悬空
        /// </summary>
        private bool HasUnsupportedOverhang(List<CellOccupancy> occupiedCells)
        {
            // 分组按Y坐标
            var groupsByHeight = occupiedCells.GroupBy(oc => oc.GridPosition.y);
            
            foreach (var heightGroup in groupsByHeight)
            {
                int currentHeight = heightGroup.Key;
                
                // 检查每个占据的单元格
                foreach (CellOccupancy occupancy in heightGroup)
                {
                    // 检查所有可能的支撑方向
                    bool hasSupport = CheckSupportInDirection(occupancy.GridPosition, Vector3Int.down) ||
                                     CheckSupportInDirection(occupancy.GridPosition, Vector3Int.left) ||
                                     CheckSupportInDirection(occupancy.GridPosition, Vector3Int.right) ||
                                     CheckSupportInDirection(occupancy.GridPosition, Vector3Int.forward) ||
                                     CheckSupportInDirection(occupancy.GridPosition, Vector3Int.back);
                    
                    if (!hasSupport && currentHeight > 0)
                    {
                        return true;
                    }
                }
            }
            
            return false;
        }
        
        /// <summary>
        /// 检查指定方向的支撑
        /// </summary>
        private bool CheckSupportInDirection(Vector3Int gridPos, Vector3Int direction)
        {
            Vector3Int checkPos = gridPos + direction;
            
            while (IsWithinGridBounds(checkPos))
            {
                if (gridCells[checkPos.x, checkPos.y, checkPos.z].IsOccupied)
                {
                    return true;
                }
                
                // 如果到达边界或遇到空洞,继续检查
                checkPos += direction;
            }
            
            return false;
        }
        
        /// <summary>
        /// 标记单元格为脏(需要更新)
        /// </summary>
        private void MarkCellDirty(Cell cell)
        {
            if (!dirtyCells.Contains(cell))
            {
                dirtyCells.Add(cell);
            }
            
            // 延迟更新,避免每帧都更新
            if (Time.time - lastUpdateTime > UPDATE_INTERVAL)
            {
                ProcessDirtyCells();
            }
        }
        
        /// <summary>
        /// 处理脏单元格
        /// </summary>
        private void ProcessDirtyCells()
        {
            foreach (Cell cell in dirtyCells)
            {
                UpdateCellVisualization(cell);
            }
            
            dirtyCells.Clear();
            lastUpdateTime = Time.time;
        }
        
        /// <summary>
        /// 创建网格可视化
        /// </summary>
        private void CreateGridVisualization()
        {
            if (gridConfig.ShowGridVisualization)
            {
                gridVisual = new GameObject("GridVisualization");
                gridVisual.transform.SetParent(transform);
                
                // 创建网格渲染器
                MeshFilter meshFilter = gridVisual.AddComponent<MeshFilter>();
                MeshRenderer meshRenderer = gridVisual.AddComponent<MeshRenderer>();
                
                // 生成网格
                Mesh gridMesh = GenerateGridMesh();
                meshFilter.mesh = gridMesh;
                
                // 设置材质
                gridMaterial = new Material(Shader.Find("Unlit/Transparent"));
                gridMaterial.color = gridConfig.GridLineColor;
                meshRenderer.material = gridMaterial;
                
                // 设置图层
                gridVisual.layer = LayerMask.NameToLayer("Grid");
            }
        }
        
        /// <summary>
        /// 生成网格线框
        /// </summary>
        private Mesh GenerateGridMesh()
        {
            Mesh mesh = new Mesh();
            List<Vector3> vertices = new List<Vector3>();
            List<int> indices = new List<int>();
            
            int width = gridCells.GetLength(0);
            int height = gridCells.GetLength(1);
            int depth = gridCells.GetLength(2);
            
            // 生成垂直线条
            for (int x = 0; x <= width; x++)
            {
                for (int z = 0; z <= depth; z++)
                {
                    Vector3 bottom = GetCellWorldPosition(x, 0, z);
                    Vector3 top = GetCellWorldPosition(x, height, z);
                    
                    vertices.Add(bottom);
                    vertices.Add(top);
                    indices.Add(vertices.Count - 2);
                    indices.Add(vertices.Count - 1);
                }
            }
            
            // 生成水平线条(X方向)
            for (int y = 0; y <= height; y++)
            {
                for (int z = 0; z <= depth; z++)
                {
                    Vector3 left = GetCellWorldPosition(0, y, z);
                    Vector3 right = GetCellWorldPosition(width, y, z);
                    
                    vertices.Add(left);
                    vertices.Add(right);
                    indices.Add(vertices.Count - 2);
                    indices.Add(vertices.Count - 1);
                }
            }
            
            // 生成水平线条(Z方向)
            for (int y = 0; y <= height; y++)
            {
                for (int x = 0; x <= width; x++)
                {
                    Vector3 front = GetCellWorldPosition(x, y, 0);
                    Vector3 back = GetCellWorldPosition(x, y, depth);
                    
                    vertices.Add(front);
                    vertices.Add(back);
                    indices.Add(vertices.Count - 2);
                    indices.Add(vertices.Count - 1);
                }
            }
            
            mesh.vertices = vertices.ToArray();
            mesh.SetIndices(indices.ToArray(), MeshTopology.Lines, 0);
            mesh.RecalculateBounds();
            
            return mesh;
        }
        
        /// <summary>
        /// 更新单元格可视化
        /// </summary>
        private void UpdateCellVisualization(Cell cell)
        {
            // 在商业项目中,这里会更新单元格的视觉效果
            // 例如:高亮被占用的单元格,显示支撑状态等
            
            if (cell.VisualInstance != null)
            {
                // 更新材质颜色
                Renderer renderer = cell.VisualInstance.GetComponent<Renderer>();
                if (renderer != null)
                {
                    Color cellColor = cell.IsOccupied ? 
                        gridConfig.OccupiedCellColor : 
                        gridConfig.EmptyCellColor;
                    
                    if (cell.IsDirty)
                    {
                        cellColor = gridConfig.DirtyCellColor;
                    }
                    
                    renderer.material.color = cellColor;
                }
                
                cell.IsDirty = false;
            }
        }
        
        /// <summary>
        /// 更新网格可视化
        /// </summary>
        private void UpdateGridVisualization()
        {
            // 在需要时重新生成网格线框
            if (gridVisual != null && gridConfig.DynamicGridUpdate)
            {
                MeshFilter meshFilter = gridVisual.GetComponent<MeshFilter>();
                if (meshFilter != null)
                {
                    meshFilter.mesh = GenerateGridMesh();
                }
            }
        }
        
        // 事件定义
        public delegate void LayerClearedHandler(int layerY, int layersCleared);
        public event LayerClearedHandler OnLayerCleared;
        
        public delegate void TetrisClearedHandler(int layersCleared);
        public event TetrisClearedHandler OnTetrisCleared;
    }
    
    /// <summary>
    /// 网格单元格
    /// </summary>
    public class Cell
    {
        public int X { get; private set; }
        public int Y { get; private set; }
        public int Z { get; private set; }
        public Vector3 WorldPosition { get; private set; }
        public Bounds Bounds { get; private set; }
        
        public bool IsOccupied { get; set; }
        public BlockController OccupyingBlock { get; set; }
        public GameObject VisualInstance { get; set; }
        public bool IsDirty { get; set; }
        public float LastUpdateTime { get; set; }
        
        public Cell(int x, int y, int z, Vector3 worldPosition)
        {
            X = x;
            Y = y;
            Z = z;
            WorldPosition = worldPosition;
            Bounds = new Bounds(worldPosition, Vector3.one * 0.9f); // 稍微小于单元格大小
            IsOccupied = false;
            IsDirty = false;
        }
    }
    
    /// <summary>
    /// 单元格占用信息
    /// </summary>
    public struct CellOccupancy
    {
        public Vector3 Position;
        public Vector3Int GridPosition;
        public BlockController Block;
        
        public CellOccupancy(Vector3 position, Vector3Int gridPos, BlockController block)
        {
            Position = position;
            GridPosition = gridPos;
            Block = block;
        }
    }
    
    /// <summary>
    /// 放置结果
    /// </summary>
    public struct PlacementResult
    {
        public bool IsValid;
        public string InvalidReason;
        public List<Cell> OccupiedCells;
        public Cell ConflictingCell;
        
        public static PlacementResult Invalid => new PlacementResult
        {
            IsValid = false,
            InvalidReason = "无效放置"
        };
    }
    
    /// <summary>
    /// 网格配置
    /// </summary>
    [System.Serializable]
    public class GridConfiguration
    {
        [Header("网格尺寸")]
        public Vector3 GridSize = new Vector3(10, 20, 5);
        public Vector3 GridCenter = Vector3.zero;
        public float CellSize = 1.0f;
        public float MinNodeSize = 2.0f; // 八叉树最小节点尺寸
        
        [Header("游戏规则")]
        public bool RequireBottomSupport = true;
        public bool EnableOverhangDetection = false;
        public float GravityStrength = 9.8f;
        
        [Header("可视化")]
        public bool ShowGridVisualization = true;
        public Color GridLineColor = new Color(0.5f, 0.5f, 0.5f, 0.3f);
        public Color OccupiedCellColor = Color.blue;
        public Color EmptyCellColor = Color.clear;
        public Color DirtyCellColor = Color.yellow;
        public bool DynamicGridUpdate = true;
    }
}

11.3.2 模块化架构设计与通信机制

在商业VR项目中,模块化架构是确保代码可维护性和团队协作效率的关键。我们采用基于组件的架构模式,每个核心功能都被封装为独立的模块,通过事件驱动的方式相互通信。

架构设计原则:

  1. 单一职责:每个模块只负责一个特定的功能
  2. 依赖倒置:高层模块不依赖低层模块,两者都依赖抽象
  3. 事件驱动:模块间通过事件系统解耦
  4. 可测试性:每个模块都可以独立测试
using UnityEngine;
using System;
using System.Collections.Generic;
using UnityEngine.Events;

namespace VR.Tetris.Architecture
{
    /// <summary>
    /// 游戏事件系统
    /// 提供模块间解耦的通信机制
    /// </summary>
    public class GameEventSystem : MonoBehaviour
    {
        private static GameEventSystem instance;
        public static GameEventSystem Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = FindObjectOfType<GameEventSystem>();
                    if (instance == null)
                    {
                        GameObject go = new GameObject("GameEventSystem");
                        instance = go.AddComponent<GameEventSystem>();
                        DontDestroyOnLoad(go);
                    }
                }
                return instance;
            }
        }
        
        // 事件字典
        private Dictionary<Type, List<object>> eventListeners = 
            new Dictionary<Type, List<object>>();
        
        // 延迟事件队列
        private Queue<DelayedEvent> delayedEvents = new Queue<DelayedEvent>();
        
        /// <summary>
        /// 注册事件监听器
        /// </summary>
        public void RegisterListener<T>(UnityAction<T> listener) where T : GameEvent
        {
            Type eventType = typeof(T);
            
            if (!eventListeners.ContainsKey(eventType))
            {
                eventListeners[eventType] = new List<object>();
            }
            
            eventListeners[eventType].Add(listener);
        }
        
        /// <summary>
        /// 取消注册事件监听器
        /// </summary>
        public void UnregisterListener<T>(UnityAction<T> listener) where T : GameEvent
        {
            Type eventType = typeof(T);
            
            if (eventListeners.ContainsKey(eventType))
            {
                eventListeners[eventType].Remove(listener);
                
                if (eventListeners[eventType].Count == 0)
                {
                    eventListeners.Remove(eventType);
                }
            }
        }
        
        /// <summary>
        /// 触发事件(立即执行)
        /// </summary>
        public void TriggerEvent<T>(T gameEvent) where T : GameEvent
        {
            Type eventType = typeof(T);
            
            if (eventListeners.ContainsKey(eventType))
            {
                // 复制列表以避免在迭代时修改
                List<object> listeners = new List<object>(eventListeners[eventType]);
                
                foreach (object listenerObj in listeners)
                {
                    UnityAction<T> listener = listenerObj as UnityAction<T>;
                    if (listener != null)
                    {
                        try
                        {
                            listener.Invoke(gameEvent);
                        }
                        catch (Exception e)
                        {
                            Debug.LogError($"事件处理出错: {e.Message}");
                        }
                    }
                }
            }
            
            // 记录事件到分析系统
            LogEventForAnalytics(gameEvent);
        }
        
        /// <summary>
        /// 触发延迟事件
        /// </summary>
        public void TriggerEventDelayed<T>(T gameEvent, float delay) where T : GameEvent
        {
            DelayedEvent delayedEvent = new DelayedEvent
            {
                Event = gameEvent,
                TriggerTime = Time.time + delay,
                EventType = typeof(T)
            };
            
            delayedEvents.Enqueue(delayedEvent);
        }
        
        /// <summary>
        /// 每帧更新
        /// </summary>
        private void Update()
        {
            // 处理延迟事件
            ProcessDelayedEvents();
        }
        
        /// <summary>
        /// 处理延迟事件
        /// </summary>
        private void ProcessDelayedEvents()
        {
            int eventsToProcess = delayedEvents.Count;
            
            for (int i = 0; i < eventsToProcess; i++)
            {
                DelayedEvent delayedEvent = delayedEvents.Dequeue();
                
                if (Time.time >= delayedEvent.TriggerTime)
                {
                    // 使用反射触发事件
                    MethodInfo method = typeof(GameEventSystem).GetMethod("TriggerEvent");
                    MethodInfo genericMethod = method.MakeGenericMethod(delayedEvent.EventType);
                    genericMethod.Invoke(this, new object[] { delayedEvent.Event });
                }
                else
                {
                    // 重新加入队列
                    delayedEvents.Enqueue(delayedEvent);
                }
            }
        }
        
        /// <summary>
        /// 记录事件用于分析
        /// </summary>
        private void LogEventForAnalytics(GameEvent gameEvent)
        {
            // 商业项目中会发送到分析服务
            AnalyticsEventData eventData = new AnalyticsEventData
            {
                EventType = gameEvent.GetType().Name,
                Timestamp = DateTime.UtcNow,
                EventData = gameEvent.GetAnalyticsData()
            };
            
            AnalyticsManager.Instance.LogEvent(eventData);
        }
        
        /// <summary>
        /// 清理所有监听器
        /// </summary>
        public void ClearAllListeners()
        {
            eventListeners.Clear();
            delayedEvents.Clear();
        }
        
        /// <summary>
        /// 延迟事件结构
        /// </summary>
        private struct DelayedEvent
        {
            public GameEvent Event;
            public float TriggerTime;
            public Type EventType;
        }
    }
    
    /// <summary>
    /// 游戏事件基类
    /// </summary>
    public abstract class GameEvent
    {
        public float Timestamp { get; private set; }
        
        protected GameEvent()
        {
            Timestamp = Time.time;
        }
        
        public virtual Dictionary<string, object> GetAnalyticsData()
        {
            return new Dictionary<string, object>
            {
                { "timestamp", Timestamp }
            };
        }
    }
    
    /// <summary>
    /// 方块放置事件
    /// </summary>
    public class BlockPlacedEvent : GameEvent
    {
        public BlockType BlockType { get; private set; }
        public Vector3 Position { get; private set; }
        public Quaternion Rotation { get; private set; }
        public float PlacementTime { get; private set; }
        
        public BlockPlacedEvent(BlockType blockType, Vector3 position, Quaternion rotation, float placementTime)
        {
            BlockType = blockType;
            Position = position;
            Rotation = rotation;
            PlacementTime = placementTime;
        }
        
        public override Dictionary<string, object> GetAnalyticsData()
        {
            Dictionary<string, object> data = base.GetAnalyticsData();
            data.Add("block_type", BlockType.ToString());
            data.Add("position", Position.ToString());
            data.Add("rotation", Rotation.eulerAngles.ToString());
            data.Add("placement_time", PlacementTime);
            return data;
        }
    }
    
    /// <summary>
    /// 层清除事件
    /// </summary>
    public class LayerClearedEvent : GameEvent
    {
        public int LayerY { get; private set; }
        public int LayersCleared { get; private set; }
        public int ScoreAwarded { get; private set; }
        
        public LayerClearedEvent(int layerY, int layersCleared, int scoreAwarded)
        {
            LayerY = layerY;
            LayersCleared = layersCleared;
            ScoreAwarded = scoreAwarded;
        }
        
        public override Dictionary<string, object> GetAnalyticsData()
        {
            Dictionary<string, object> data = base.GetAnalyticsData();
            data.Add("layer_y", LayerY);
            data.Add("layers_cleared", LayersCleared);
            data.Add("score_awarded", ScoreAwarded);
            return data;
        }
    }
    
    /// <summary>
    /// 游戏状态变更事件
    /// </summary>
    public class GameStateChangedEvent : GameEvent
    {
        public GameState PreviousState { get; private set; }
        public GameState NewState { get; private set; }
        
        public GameStateChangedEvent(GameState previousState, GameState newState)
        {
            PreviousState = previousState;
            NewState = newState;
        }
        
        public override Dictionary<string, object> GetAnalyticsData()
        {
            Dictionary<string, object> data = base.GetAnalyticsData();
            data.Add("previous_state", PreviousState.ToString());
            data.Add("new_state", NewState.ToString());
            return data;
        }
    }
    
    /// <summary>
    /// 输入事件
    /// </summary>
    public class InputEvent : GameEvent
    {
        public InputType InputType { get; private set; }
        public Vector3 InputPosition { get; private set; }
        public Quaternion InputRotation { get; private set; }
        public float InputStrength { get; private set; }
        
        public InputEvent(InputType inputType, Vector3 position, Quaternion rotation, float strength)
        {
            InputType = inputType;
            InputPosition = position;
            InputRotation = rotation;
            InputStrength = strength;
        }
        
        public override Dictionary<string, object> GetAnalyticsData()
        {
            Dictionary<string, object> data = base.GetAnalyticsData();
            data.Add("input_type", InputType.ToString());
            data.Add("input_position", InputPosition.ToString());
            data.Add("input_strength", InputStrength);
            return data;
        }
    }
    
    /// <summary>
    /// 服务定位器
    /// 提供全局服务访问
    /// </summary>
    public class ServiceLocator : MonoBehaviour
    {
        private static ServiceLocator instance;
        private Dictionary<Type, object> services = new Dictionary<Type, object>();
        
        public static ServiceLocator Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = FindObjectOfType<ServiceLocator>();
                    if (instance == null)
                    {
                        GameObject go = new GameObject("ServiceLocator");
                        instance = go.AddComponent<ServiceLocator>();
                        DontDestroyOnLoad(go);
                    }
                }
                return instance;
            }
        }
        
        /// <summary>
        /// 注册服务
        /// </summary>
        public void RegisterService<T>(T serviceInstance) where T : class
        {
            Type serviceType = typeof(T);
            
            if (services.ContainsKey(serviceType))
            {
                Debug.LogWarning($"服务 {serviceType.Name} 已注册,将被覆盖");
            }
            
            services[serviceType] = serviceInstance;
            Debug.Log($"服务注册: {serviceType.Name}");
        }
        
        /// <summary>
        /// 获取服务
        /// </summary>
        public T GetService<T>() where T : class
        {
            Type serviceType = typeof(T);
            
            if (services.ContainsKey(serviceType))
            {
                return services[serviceType] as T;
            }
            
            // 尝试查找场景中的实例
            T foundService = FindObjectOfType<T>();
            if (foundService != null)
            {
                RegisterService(foundService);
                return foundService;
            }
            
            Debug.LogError($"未找到服务: {serviceType.Name}");
            return null;
        }
        
        /// <summary>
        /// 注销服务
        /// </summary>
        public void UnregisterService<T>() where T : class
        {
            Type serviceType = typeof(T);
            
            if (services.ContainsKey(serviceType))
            {
                services.Remove(serviceType);
                Debug.Log($"服务注销: {serviceType.Name}");
            }
        }
        
        /// <summary>
        /// 清理所有服务
        /// </summary>
        public void ClearAllServices()
        {
            services.Clear();
            Debug.Log("所有服务已清理");
        }
        
        /// <summary>
        /// 初始化核心服务
        /// </summary>
        public void InitializeCoreServices()
        {
            // 确保事件系统存在
            GameEventSystem eventSystem = GameEventSystem.Instance;
            RegisterService(eventSystem);
            
            // 确保输入管理器存在
            InputManager inputManager = InputManager.Instance;
            if (inputManager != null)
            {
                RegisterService(inputManager);
            }
            
            // 确保音频管理器存在
            AudioManager audioManager = AudioManager.Instance;
            if (audioManager != null)
            {
                RegisterService(audioManager);
            }
            
            // 确保UI管理器存在
            UIManager uiManager = UIManager.Instance;
            if (uiManager != null)
            {
                RegisterService(uiManager);
            }
            
            Debug.Log("核心服务初始化完成");
        }
    }
    
    /// <summary>
    /// 模块基类
    /// </summary>
    public abstract class GameModule : MonoBehaviour
    {
        protected GameEventSystem eventSystem;
        protected ServiceLocator serviceLocator;
        
        /// <summary>
        /// 初始化模块
        /// </summary>
        public virtual void Initialize()
        {
            eventSystem = GameEventSystem.Instance;
            serviceLocator = ServiceLocator.Instance;
            
            RegisterEventListeners();
            
            Debug.Log($"{GetType().Name} 模块初始化完成");
        }
        
        /// <summary>
        /// 注册事件监听器
        /// </summary>
        protected abstract void RegisterEventListeners();
        
        /// <summary>
        /// 清理模块
        /// </summary>
        public virtual void Cleanup()
        {
            UnregisterEventListeners();
            
            Debug.Log($"{GetType().Name} 模块清理完成");
        }
        
        /// <summary>
        /// 取消注册事件监听器
        /// </summary>
        protected abstract void UnregisterEventListeners();
        
        /// <summary>
        /// 获取服务
        /// </summary>
        protected T GetService<T>() where T : class
        {
            return serviceLocator.GetService<T>();
        }
        
        /// <summary>
        /// 触发事件
        /// </summary>
        protected void TriggerEvent<T>(T gameEvent) where T : GameEvent
        {
            eventSystem.TriggerEvent(gameEvent);
        }
    }
    
    /// <summary>
    /// 方块管理器模块
    /// </summary>
    public class BlockManagerModule : GameModule
    {
        private List<BlockController> activeBlocks = new List<BlockController>();
        private BlockSpawner spawner;
        
        [SerializeField]
        private BlockPoolConfiguration poolConfig;
        
        private ObjectPool<BlockController> blockPool;
        
        /// <summary>
        /// 初始化
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();
            
            // 初始化对象池
            InitializeBlockPool();
            
            // 获取生成器
            spawner = GetService<BlockSpawner>();
            if (spawner != null)
            {
                spawner.Initialize(this);
            }
            
            Debug.Log("方块管理器初始化完成");
        }
        
        /// <summary>
        /// 初始化方块对象池
        /// </summary>
        private void InitializeBlockPool()
        {
            blockPool = new ObjectPool<BlockController>(
                CreateBlockInstance,
                OnBlockTakenFromPool,
                OnBlockReturnedToPool,
                DestroyBlockInstance,
                poolConfig.PreloadCount,
                poolConfig.MaxPoolSize
            );
            
            Debug.Log($"方块对象池初始化: 预加载{poolConfig.PreloadCount}个实例");
        }
        
        /// <summary>
        /// 创建方块实例
        /// </summary>
        private BlockController CreateBlockInstance()
        {
            // 创建通用方块预制体
            GameObject blockObj = new GameObject("Block");
            BlockController block = blockObj.AddComponent<BlockController>();
            
            // 添加必要的组件
            blockObj.AddComponent<Rigidbody>();
            blockObj.AddComponent<BoxCollider>();
            
            // 设置为不活跃
            blockObj.SetActive(false);
            
            return block;
        }
        
        /// <summary>
        /// 从对象池获取方块
        /// </summary>
        public BlockController GetBlockFromPool(BlockType blockType, Vector3 position, Quaternion rotation)
        {
            BlockController block = blockPool.Get();
            
            if (block != null)
            {
                // 配置方块
                block.Initialize(blockType);
                block.transform.position = position;
                block.transform.rotation = rotation;
                block.gameObject.SetActive(true);
                
                // 添加到活动列表
                activeBlocks.Add(block);
                
                // 触发事件
                TriggerEvent(new BlockSpawnedEvent(blockType, position, rotation));
            }
            
            return block;
        }
        
        /// <summary>
        /// 返回方块到对象池
        /// </summary>
        public void ReturnBlockToPool(BlockController block)
        {
            if (block != null)
            {
                // 从活动列表移除
                activeBlocks.Remove(block);
                
                // 重置方块状态
                block.ResetState();
                block.gameObject.SetActive(false);
                
                // 返回对象池
                blockPool.Release(block);
                
                // 触发事件
                TriggerEvent(new BlockDespawnedEvent(block.BlockType));
            }
        }
        
        /// <summary>
        /// 方块从池中取出时的处理
        /// </summary>
        private void OnBlockTakenFromPool(BlockController block)
        {
            // 可以在这里执行初始化逻辑
            block.gameObject.SetActive(true);
        }
        
        /// <summary>
        /// 方块返回池中的处理
        /// </summary>
        private void OnBlockReturnedToPool(BlockController block)
        {
            // 可以在这里执行清理逻辑
            block.gameObject.SetActive(false);
        }
        
        /// <summary>
        /// 销毁方块实例
        /// </summary>
        private void DestroyBlockInstance(BlockController block)
        {
            if (block != null)
            {
                Destroy(block.gameObject);
            }
        }
        
        /// <summary>
        /// 注册事件监听器
        /// </summary>
        protected override void RegisterEventListeners()
        {
            eventSystem.RegisterListener<BlockPlacedEvent>(OnBlockPlaced);
            eventSystem.RegisterListener<LayerClearedEvent>(OnLayerCleared);
            eventSystem.RegisterListener<GameOverEvent>(OnGameOver);
        }
        
        /// <summary>
        /// 取消注册事件监听器
        /// </summary>
        protected override void UnregisterEventListeners()
        {
            eventSystem.UnregisterListener<BlockPlacedEvent>(OnBlockPlaced);
            eventSystem.UnregisterListener<LayerClearedEvent>(OnLayerCleared);
            eventSystem.UnregisterListener<GameOverEvent>(OnGameOver);
        }
        
        /// <summary>
        /// 方块放置事件处理
        /// </summary>
        private void OnBlockPlaced(BlockPlacedEvent evt)
        {
            // 更新方块状态
            // 商业项目中可能会更新方块的可交互状态
        }
        
        /// <summary>
        /// 层清除事件处理
        /// </summary>
        private void OnLayerCleared(LayerClearedEvent evt)
        {
            // 清除被标记为销毁的方块
            CleanupDestroyedBlocks();
        }
        
        /// <summary>
        /// 游戏结束事件处理
        /// </summary>
        private void OnGameOver(GameOverEvent evt)
        {
            // 清理所有方块
            ClearAllBlocks();
        }
        
        /// <summary>
        /// 清理所有方块
        /// </summary>
        public void ClearAllBlocks()
        {
            foreach (BlockController block in activeBlocks.ToArray())
            {
                ReturnBlockToPool(block);
            }
            
            activeBlocks.Clear();
        }
        
        /// <summary>
        /// 清理被销毁的方块
        /// </summary>
        private void CleanupDestroyedBlocks()
        {
            for (int i = activeBlocks.Count - 1; i >= 0; i--)
            {
                BlockController block = activeBlocks[i];
                if (block == null || block.ShouldBeDestroyed)
                {
                    if (block != null)
                    {
                        ReturnBlockToPool(block);
                    }
                    activeBlocks.RemoveAt(i);
                }
            }
        }
        
        /// <summary>
        /// 获取活跃方块数量
        /// </summary>
        public int GetActiveBlockCount()
        {
            return activeBlocks.Count;
        }
        
        /// <summary>
        /// 获取对象池统计
        /// </summary>
        public PoolStatistics GetPoolStatistics()
        {
            return new PoolStatistics
            {
                ActiveCount = activeBlocks.Count,
                PooledCount = blockPool.CountInactive,
                TotalCreated = blockPool.CountAll
            };
        }
    }
    
    /// <summary>
    /// 对象池配置
    /// </summary>
    [System.Serializable]
    public class BlockPoolConfiguration
    {
        public int PreloadCount = 20;
        public int MaxPoolSize = 100;
        public bool AutoExpand = true;
        public int ExpandAmount = 10;
    }
    
    /// <summary>
    /// 对象池统计
    /// </summary>
    public struct PoolStatistics
    {
        public int ActiveCount;
        public int PooledCount;
        public int TotalCreated;
        
        public override string ToString()
        {
            return $"对象池统计: 活跃={ActiveCount}, 池中={PooledCount}, 总计={TotalCreated}";
        }
    }
    
    // 枚举定义
    public enum BlockType
    {
        I, J, L, O, S, T, Z
    }
    
    public enum GameState
    {
        Menu,
        Playing,
        Paused,
        GameOver
    }
    
    public enum InputType
    {
        Grab,
        Rotate,
        Menu,
        Touchpad
    }
}

由于篇幅限制,本文展示了游戏系统架构和模块化设计的关键部分。在完整的商业VR项目中,还需要实现UI系统、排行榜功能、多人游戏支持、云存档等高级功能。每个模块都需要经过充分的测试和性能优化,确保在HTC VIVE等VR设备上提供流畅的体验。

接下来的开发工作应包括:

  1. 完善方块物理行为
  2. 实现高级视觉效果(粒子系统、发光效果)
  3. 添加背景音乐和环境音效
  4. 实现成就系统
  5. 添加教程和新手引导
  6. 进行跨平台适配测试

通过模块化架构和事件驱动设计,项目可以保持良好的扩展性和可维护性,便于后续功能迭代和团队协作开发。

Logo

这里是一个专注于游戏开发的社区,我们致力于为广大游戏爱好者提供一个良好的学习和交流平台。我们的专区包含了各大流行引擎的技术博文,涵盖了从入门到进阶的各个阶段,无论你是初学者还是资深开发者,都能在这里找到适合自己的内容。除此之外,我们还会不定期举办游戏开发相关的活动,让大家更好地交流互动。加入我们,一起探索游戏开发的奥秘吧!

更多推荐