unity 简单实现三阶魔方游戏

news2025/1/10 11:56:39

unity 简单实现三阶魔方游戏

魔方体验地址

工程文件免费下载

实现思路

一、魔方的旋转

三阶魔方由26个方块与 9个旋转轴组成。旋转轴旋转时带动在其控制范围的方块旋转。

旋转轴如何带动方块旋转?

把旋转轴控制范围内的方块设置成旋转轴的子物体,旋转轴旋转时便会带动子物体旋转。

如何得到旋转轴控制范围内的方块?

这里运用的unity提供的一个API方法Collider.bounds.Intersects,另一个包围盒是否与该包围盒交叠。即为每一个方块和旋转轴添加Collider,从而判断方块是否为旋转轴控制范围内的方块。

在这里插入图片描述

方块添加Collider

在这里插入图片描述

旋转轴添加Collider

二、魔方的控制

使用鼠标拖拽魔方旋转如下图

旋转轴的确定

确定的规律

  1. 每一个旋转轴的旋转方向是固定的;
  2. 每一个方块都有三个旋转轴控制。
旋转轴的确定

用鼠标点击魔方的两个点,即鼠标点击点和鼠标抬起点,来做判断依据。两点与中心点组成的面的法线即是旋转轴的旋转法线。

如图,A点是点击的第一个点,B点是点击的第二个点,O点是原点。

OAB组成的面就是的法线就是 旋转轴的旋转法线。

旋转方向的确定

如上图叉乘 O A ⃗ \vec{OA} OA × \times × O B ⃗ \vec{OB} OB 得到法线如果是正值正向旋转,为负值逆向旋转。

   public void ToRotate(List<AxisControl> axisControls)
    {
        Vector3 vector =Vector3.Normalize( Vector3.Cross(FirstPoint - transform.position, SecondPoint - transform.position));//向量叉乘
        int length = axisControls.Count;
        for (int i = 0; i < length; i++)
        {
            if (axisControls[i].rotationFront.ToString() == ReturnRotate(vector))
            {
                axisControls[i].ToRotate(front);
                break;
            }
        }
    }
    int front = 1;
    string ReturnRotate( Vector3 vector)
    {

        float nx = Mathf.Abs(vector.x);
        float ny = Mathf.Abs(vector.y);
        float nz = Mathf.Abs(vector.z);

        if (nx> ny&& nx> nz)
        {
            vector.x = vector.x > 0 ? front = 1 : front = -1;
            return "X";
        }
        else if (ny > nx && ny > nz)
        {
            vector.y = vector.y > 0 ? front = 1 : front = -1;
            return "Y";
        }
      else  
        {
            vector.z = vector.z > 0 ? front = 1 : front = -1;
            return "Z";
        }
    }

完整代码

方块的控制脚本(绑到每个方块上)

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

public class CubeControl : MonoBehaviour
{
  public   List<AxisControl> AxisControls;
    private Collider _collider;

    public Collider Collider
    {
        get
        {
            if (_collider == null)
            {
                _collider = gameObject.GetComponent<Collider>();
            }
            return _collider;
        }
    }
    RubikManager rubikManager;

    public RubikManager RubikManager
    {
        get
        {
            if (rubikManager == null)
            {
                rubikManager = RubikManager.Instance;
            }
            return rubikManager;
        }
    }

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

    public void AddEventTrigger()
    {
        gameObject.AddComponent<EventTrigger>();
        addEventTrigger(transform, EventTriggerType.PointerUp, UpCude);
        addEventTrigger(transform, EventTriggerType.PointerDown, DownCude);
    }
    /// <summary>
    /// event事件绑定的方法
    /// </summary>
    /// <param name="insObject">事件物体</param>
    /// <param name="eventType">事件类型</param>
    /// <param name="myFunction">事件需要的回调方法</param>
    public void addEventTrigger(Transform insObject, EventTriggerType eventType, UnityEngine.Events.UnityAction<BaseEventData> myFunction)//泛型委托
    {
        EventTrigger trigger = insObject.GetComponent<EventTrigger>();
        EventTrigger.Entry entry = new EventTrigger.Entry();
        entry.eventID = eventType;
        entry.callback.AddListener(myFunction);
        trigger.triggers.Add(entry);
    }
   
    void DownCude(BaseEventData data)
    {
        if (Input.GetMouseButtonDown(0))
        {
            RubikManager.FirstPoint = RayPiont();
        }
    }
    Vector3 front = Vector3.zero;
    void UpCude(BaseEventData data)
    {
        if (!RubikManager.isOperation&& Input.GetMouseButtonUp(0))
        {
      
        Vector3 rayPiont = RayPiont();
        RubikManager.SecondPoint = rayPiont;
        List<AxisControl> axisControls = new List<AxisControl>();
        int length = AxisControls.Count;
         
                RubikManager.ToRotate(AxisControls);
                RubikManager.isOperation = true;
        }
    }

    Vector3 RayPiont()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit raycastHit;
        if (Physics.Raycast(ray,out raycastHit))
        {
            return raycastHit.point;
        }
        else
        {
            return Vector3.zero;
        }
    }
    public void SetParent(Transform transformSelf)
    {
        transform.parent = transformSelf;
    }
}

旋转轴的控制方法(绑在旋转轴上)

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


public class AxisControl : MonoBehaviourSimplify
{
    public enum RotationFront
    {
        X, Y,Z
    }
    public List<CubeControl> Cubes;
    public RotationFront rotationFront;
     Collider _collider;
    RubikManager rubikManager;
    Vector3 vector = Vector3.zero;
    public RubikManager RubikManager
    {
        get
        {
            if (rubikManager==null)
            {
                rubikManager = RubikManager.Instance;
            }
            return rubikManager;
        }
    }

    public Collider Collider
    {
        get
        {
            if (_collider==null)
            {
                _collider = gameObject.GetComponent<Collider>();
            }
            return _collider;
        } }


    // Start is called before the first frame update
    void Start()
    {
        Delay(1, () => { FindSelfCube(); });
        vector = transform.eulerAngles;
    }

    public void FindSelfCube()
    {
        Cubes = new List<CubeControl>() ;
        List<CubeControl> allCubeControls = RubikManager.CubeControls;
        int length = allCubeControls.Count;
        for (int i = 0; i < length; i++)
        {
            if (Collider.bounds.Intersects(allCubeControls[i].Collider.bounds))//得到旋转轴控制范围内的方块
            {
                Cubes.Add(allCubeControls[i]);
                allCubeControls[i].AxisControls.Add(this);
            }
        }
    }

    public override void OnBeforeDestroy()
    {
        
    }
    public void ToRotate(int front)
    {
        int length = Cubes.Count;
        for (int i = 0; i < length; i++)
        {
            Cubes[i].SetParent(transform);
        }
       
        if (rotationFront== RotationFront.X)
        {
             vector += (front* new Vector3(90, 0, 0));
            if (vector.x>=360)
            {
                vector = Vector3.zero;
            }
         
           
        }
        if (rotationFront == RotationFront.Y)
        {
            vector += (front * new Vector3(0, 90, 0));
            if (vector.y>= 360)
            {
                vector = Vector3.zero;
            }

        }
        if (rotationFront == RotationFront.Z)
        {
            vector += (front * new Vector3(0, 0, 90));
            if (vector.z >= 360)
            {
                vector = Vector3.zero;
            }
        }
        DoRotate(transform, vector, 0.5f,()=> { RubikManager.Reset(); });
    }
}

魔方的总控

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

public class RubikManager : Singleton<RubikManager>
{
    public List<AxisControl> AxisControls;
    public List<CubeControl> CubeControls;


    public Vector3 FirstPoint;
    public Vector3 SecondPoint;

    public override void OnBeforeDestroy() { }
    public bool isOperation = false;

    // Start is called before the first frame update
    void Start()
    {
        GameObject[] AllCubes = GameObject.FindGameObjectsWithTag("Cube");

        int cubeLength = AllCubes.Length;
        for (int i = 0; i < cubeLength; i++)
        {
            CubeControls.Add(AllCubes[i].GetComponent<CubeControl>());
        }

        GameObject[] AllAxis = GameObject.FindGameObjectsWithTag("Axis");
        int axisLength = AllAxis.Length;
        for (int j = 0; j < axisLength; j++)
        {
            AxisControls.Add(AllAxis[j].GetComponent<AxisControl>());
        }
    }
   public void ToRotate(List<AxisControl> axisControls)
    {
        Vector3 vector =Vector3.Normalize( Vector3.Cross(FirstPoint - transform.position, SecondPoint - transform.position));
        int length = axisControls.Count;
        for (int i = 0; i < length; i++)
        {
            if (axisControls[i].rotationFront.ToString() == ReturnRotate(vector))
            {
                axisControls[i].ToRotate(front);
                break;
            }
        }
    }
    int front = 1;
    string ReturnRotate( Vector3 vector)
    {

        float nx = Mathf.Abs(vector.x);
        float ny = Mathf.Abs(vector.y);
        float nz = Mathf.Abs(vector.z);

        if (nx> ny&& nx> nz)
        {
            vector.x = vector.x > 0 ? front = 1 : front = -1;
            return "X";
        }
        else if (ny > nx && ny > nz)
        {
            vector.y = vector.y > 0 ? front = 1 : front = -1;
            return "Y";
        }
      else  
        {
            vector.z = vector.z > 0 ? front = 1 : front = -1;
            return "Z";
        }
    }
    //重置
    public void Reset()
    {
        int Count = CubeControls.Count;
        for (int j = 0; j < Count; j++)
        {
            CubeControls[j].AxisControls.Clear();
        }
        int length = AxisControls.Count;
        for (int i = 0; i < length; i++)
        {
            AxisControls[i].FindSelfCube();
        }
        isOperation = false;
    }
}

单例

using UnityEngine;
namespace FrameWorkSong
{

    public abstract class Singleton <T>: MonoBehaviourSimplify where T:Singleton<T>
    {
        protected static T mInstance=null;
        public static T Instance
        {
            get
            {
                if (mInstance == null)
                {
                    mInstance = FindObjectOfType<T>();
                    if (mInstance==null)
                    {
                        var instanceName = typeof(T).Name;
                        var instanceObj = GameObject.Find(instanceName);
                        if (!instanceObj)
                        {
                            instanceObj= new GameObject(instanceName);
                        }
                        mInstance = instanceObj.AddComponent<T>();
                        DontDestroyOnLoad(instanceObj);
                        Debug.LogFormat("创建新的{0}单例实体", instanceName);
                    }
                    else
                    {
                        Debug.LogFormat("已经有单例实体");
                    }
                }
                return mInstance;
            }
        }
        protected virtual void onDestroy()
        {
            mInstance = null;
        }
    }
}

延时功能

using System;
using System.Collections;
using UnityEngine;
namespace FrameWorkSong
{
    public partial class MonoBehaviourSimplify : MonoBehaviour
    {
        /// <summary>
        /// 清理缓存
        /// </summary>
        protected void ReleaseMemory()
        {
            Resources.UnloadUnusedAssets();
            GC.Collect();
        }
        protected string GetNowTime()
        {
            return DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");
        }
        /// <summary>
        /// 延时功能
        /// </summary>
        /// <param name="seconds">延迟时间</param>
        /// <param name="onFinished">调用方法</param>
        public void Delay(float seconds, Action onFinished)
        {
            StartCoroutine(DelayCoroutione(seconds, onFinished));
        }
        private IEnumerator DelayCoroutione(float seconds, Action onFinished)
        {
            yield return new WaitForSeconds(seconds);
            onFinished();
        }
    }
}

旋转方法

using System;
using System.Collections;
using UnityEngine;
namespace FrameWorkSong
{
    public partial class MonoBehaviourSimplify
    {
/// <summary>
        /// 旋转
        /// </summary>
        /// <param name="transformSelf"></param>
        /// <param name="target"></param>
        /// <param name="time"></param>
        /// <param name="id"></param>
        /// <param name="action"></param>
        public void DoRotate(Transform transformSelf, Quaternion target, float time, Action action = null, string id = null)
        {
            IEnumerator coroutine;
            coroutine = DoRotateIE(transformSelf, target, time, action);
            StartCoroutine(coroutine);
            if (id != null)
            {
                keyValueCoroutine.AddData(id, coroutine);
            }
        }
        public void DoRotate(Transform transformSelf, Vector3 target, float time, Action action = null, string id = null)
        {
            
            DoRotate(transformSelf, Quaternion.Euler(target), time, action, id);
        }
        IEnumerator DoRotateIE(Transform transformSelf, Quaternion targetQua, float time, Action action = null)
        {
            Quaternion formQua = transformSelf.rotation;
            float t = 0;
            while (t < 1)
            {
                t += Time.deltaTime / time;
                t = t > 1 ? 1 : t;
                transform.rotation = Quaternion.Lerp(formQua, targetQua, t);
                yield return null;
            }
            transform.rotation = targetQua;
            if (action != null)
            {
                action();
            }
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/139659.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

必背经典!这些软件测试面试题及答案别放过!

对于很多软件测试新手来说&#xff0c;技术面试往往是整个面试体系里最让人头疼的部分&#xff0c;今天我为选取了软件测试面试中&#xff0c;一些经典的问题和答案&#xff0c;供大家参考使用。并且&#xff0c;还给你们奉上了一个免费的面试刷题小程序哟&#xff01;拿走不谢…

调试前端代码二三事--(一)-调试基础

一&#xff0c;在网页上调试代码 代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport…

房产管理系统中CAD图形管理分析

随着技术的不断进步和升级&#xff0c;以及高校房屋建筑物数量的不断扩充&#xff0c;建立房屋资产管理信息系统进行信息化、数字化、图形化房屋资产管理已经是势在必行。学校有庞大的房屋数据、大量的CAD图纸、复杂的房屋分类&#xff0c;建设房屋管理系统&#xff0c;能够加强…

JavaSE高级篇:运行时数据区

文章整理自深入理解Java虚拟机第一章概述第二章&#xff1a;运行时数据区域一&#xff1a;程序计数器二&#xff1a;Java虚拟机栈三&#xff1a;Java堆四&#xff1a;方法区五&#xff1a;运行时常量池六&#xff1a;直接内存第一章概述 Java程序员把内存控制的权利交给了JVM …

Spring MVC 获取参数

1..获取参数 得到单个参数&#xff1a; 运行结果&#xff1a; 得到俩个参数&#xff1a; 运行结果&#xff1a; 得到多个参数&#xff08;对象&#xff09; 运行结果&#xff1a; 通过表单传递参数&#xff1a; 但是&#xff0c;如果我用json传递就不行了&#xff0c;namenul…

【回答问题】ChatGPT上线了!给我推荐20个比较流行的3D点云分割模型

给我推荐20个比较流行的点云 3D 分割模型 这里是 20 个比较流行的点云 3D 分割模型: PointNet PointNet PointCNN Kd-Net SpiderCNN PointSIFT PointASNL PointGroup PointConv PointWeb PointGNN PointPillars PointSESA PointSNE DensePoint PointSAP PointSGN PointGCN Po…

Java 应用与数据库的关系

1.什么是数据库• 数据库就是用来存储和管理数据的仓库• 数据库存储数据的优先。2.数据库的优点• 可存储大量数据;方便检索;• 保持数据的一致性、完整性﹔安全&#xff0c;可共享;• 通过组合分析,可产生新数据。3.数据库的发展历程• 没有数据库,使用磁盘文件存储数据,层次…

2022年度AI亮点项目大起底,ChatGPT是你心目中的第一吗?

回顾2022年&#xff0c;人工智能取得了巨大进步&#xff0c;我有一种感觉&#xff0c;人工智能正在扭曲我的时间感官。谁能相信Stable Diffusion只有4个月大&#xff0c;而ChatGPT的出现才一个多月&#xff1f;感觉只是眨了眨眼&#xff0c;我们差点错过了一个全新的行业。 在过…

Effective_Objective-C_4协议与分类】

文章目录前言23.通过委托与数据源协议进行对象间的通信协议委托模式数据源模式要点总结24.将类的实现代码分散到便于管理的数个分类之中分类Xcode创建一个分类分类需要注意什么要点25.总是为第三方的分类名称加前缀要点26.切勿在分类里面声明属性关联对象扩展可以添加属性要点2…

用javascript分类刷leetcode20.字符串(图文视频讲解)

1143. 最长公共子序列 (medium) 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删…

系分 - UML【概念】

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录UML - Unified Modeling LanguageUML中有4种事物结构事物行为事物分组事物注释事物UML图的分类结构型图&#xff08;静态图&#xff09;行为型图&#xff08;动态图&#xff09;UML图 - 静态图[结构型]类图…

C#枚举器和迭代器

C#枚举器和迭代器 使用foreach语句时&#xff0c;可以依次取出数组里面的元素&#xff0c;原因就是数组提供了“枚举器&#xff08;Enumerator&#xff09;”&#xff0c;枚举器知道元素的位置并返回请求项。 枚举器IEnumerator 枚举器实现了IEnumerator接口&#xff0c;该接…

Angular页面使用指令和路由守卫进行权限控制

在各种业务系统中&#xff0c;为了保证业务及数据安全&#xff0c;除了要求用户必须登录后才能操作外&#xff0c;还针对不同的角色对不同用户设置了各自的访问权限&#xff0c;包括确定的某个页面的权限和页面中特定元素的权限。本文记录了一种Angular页面常用的权限管理方法。…

C++:std::function模板类

一&#xff1a;function定义 类模板 std::function是一种通用的多态函数包装器&#xff0c;它的实例可以对任何可以调用的目标实体进行存储&#xff0c;复制和调用操作。简单的来说&#xff1a;C中有几种可调用对象&#xff1a;函数&#xff0c;指针&#xff0c;lambda表达式&…

区块链之开发命令行操作模块

文章目录功能介绍go语言中flag用法简介项目命令行具体实现链接&#xff1a; 区块链项目github地址项目目前进度&#xff1a;功能介绍 利用命令行操作区块链相较于图形用户界面来说&#xff0c;编写代码简单&#xff0c;同时也可以实现复杂的功能。命令行模块的功能应该满足&am…

Java学习笔记 --- JDBC(1)

一、JDBC概述 基本介绍 1、JDBC为访问不同的数据库提供了统一的接口&#xff0c;为使用者屏蔽了细节问题 2、Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统&#xff0c;从而完成对数据库的各种操作 3、JDBC原理图 JDBC带来的好处 JDBC是Java提供一套用于数…

安卓移动端调用自然语言处理nlp模型【示例+源码】

安卓可以使用许多不同的方法来调用NLP模型。其中一种方法是使用现有的自然语言处理库,例如 Apache OpenNLP、 Stanford NLP 和 NLTK。这些库提供了许多常用的 NLP 功能,如分词、词干化、命名实体识别和词性标注。另一种方法是使用 TensorFlow Lite 或其他机器学习框架来加载并…

[ins 2022] 针对已见和未见群体的群体推荐中的贝叶斯归纳学习

Bayesian inductive learning in group recommendations for seen and unseen groupshttps://www.sciencedirect.com/science/article/pii/S0020025522008933摘要群组推荐是指向一组用户&#xff08;即成员&#xff09;推荐物品。在预测相关项目时&#xff0c;模型通常会面临未…

fs 文件系统模块

1、什么是 fs 文件系统模块 fs 模块是 Node.js 官方提供的、用来操作文件的模块。它提供了一系列的方法和属性&#xff0c;用来满足用户对文件的操作需求。 方法名 说明 fs.readFile() 用来读取指定文件中的内容 fs.writeFile() 用来向指定的文件中写入内容 如果要在 J…

webflux整合swagger教程V2版

1. yml文件配置 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId><version>2.1.0.RELEASE</version></dependency><!--数据库开始--&…