Java — Кубик рубика на Unity


Rubik’s cube Unity 5 [closed]

I’m developing a game for Android using the Unity 5, the game is the Rubik’s cube. I’m not using touch to move the cube, but buttons to make the movement of the cube’s rows and columns and for him all rotate got another set of buttons. My problem is this second set of buttons they have to have two functions a rotate the whole cube and another that depends on another button that when precionado it changes the function to u choose the row or column you want to move is.

closed as too broad by Basic, Alexei Levenkov, rutter, Ken White, Makoto Oct 18 ’15 at 5:14

Please edit the question to limit it to a specific problem with enough detail to >If this question can be reworded to fit the rules in the help center, please edit the question.

1 Answer 1

Your question is not very clear. Even if English isn’t your first language, code is international. Please try to post what you’ve done so far. We don’t know if you can render your cube, how the objects are organised or how they should be animated.

In short, you will need to complete at least some of the following:

  • Render a button on screen using uGUI Buttons.
  • Add a click handler to the button.
  • In the click handler, update some variables/model/similar with the new positions.
  • Animate the cube to reflect the new positions.
  • Either rotate the camera around the cube or rotate the cube and background together.

Unless you provide more information, that’s the best answer we can give you.

Re Comments:

Have a variable called locked that keeps track of if we’re locked or not. Have a function to toggle it. Wire the «Locked» button up to ToggleLocked .

And then in your button methods, check if we’re locked before doing anything.

Java — Кубик рубика на Unity

Unity Cube Simulator 3x3x3

To download the whole project you can get it from the following link:

Welcome to the Cube Simulator starter guide.

This is a short guide where I will be explaining how I made this simulator and the methodology behind it as well some technical things , it is recommended before you start that you have basic to average understanding of Unity and at least average experience in scripting/programming using C# , you should note that this guide will not discuss the source code from scratch but will only highlight major points where a developer will need to know in order to understand the flow of the simulator.

####Table of Contents :

I. Cube Construction

II. 2D Cube Logic

III. 3D Cube Logic

IV. Cube Scrambling Algorithm

V. Cube Solving Algorithm

VI. Trail Rendering

VII. Inputs

I. Cube Construction

Creating the cube is a very simple and straight forward process, you only need to use cubes, 26 of them and place them just like how the rubik cube looks , then you will need tiles which can be again formed from cubes themselves , these tiles are used in order to represent the cell color. You essentially need 3 tiles for each corner piece , 2 tiles for each edge piece , 1 tile for each center piece , you need to make sure you map and parent them correctly to their respective cubes, you may see the hierarchy in the project at Game scene or prefab for a better understanding. Materials are finally used to paint the cube , simple drag and drop operation , no biggy. :)

After creating all your pieces , remember to give each of them a tag. Ie (Center Piece 1 , Corner Piece 4. etc) , you also need to parent all the cubies under one parent which is Rubik_cube or whatever name you like , that is where you will throw in the CubeController script.

II. 2D Cube logic (Cube2D.cs)


Now you might be asking , why do we need this? Isn’t it easier to just use colliders and triggers to detect cubies on some face and then parent them to that face and rotate the face?

This is also correct and can be done , however the main reason we need a 2D Cube Logic is because we might want to add extra functionality in the future , take for example detecting the «Solved» state of the cube , there also might be people who would like to practice solving certain states of the cube , a small convinient script can be written for that too , so it’s just simply for future improvements and this is why I will be using it in the project.

There are different approaches that can be used to simulate a 2D cube , one of the easiest approaches is to represent the cube in 2D manner having three 3×3 arrays to represent the cubes , as we’re using unity it will be easier to simply use tags and in this case we can fill the array with just the tag names respectively.

Next in the code we simulate the 2D cube rotations via swapping , here’s a snippet to rotate the red face:

Another thing that is needed which is to give out the selected face tags to the Cube Controller class that will use this information to map cubies to the selected to be rotated face, here’s a snippet :

III. 3D Cube logic (CubeController.cs)

This is the primary class that controls our 3D cube , it uses the 2D cube class to logically and correctly select the correct pieces to be rotated. Here’s a list of features it includes:

  • Cube face rotation calculation and whole cube rotation.
  • Cube scrambling algorithm.
  • Trail toggle.
  • Keyboard inputs.
  • Buttons input.
  • Reset Cube.
  • Timer.

The main idea here is to use the tags and then match the cubies to their to be rotated parent , as cubies cannot have more than one parent , it is understood that if we place cubies as children to some parent and then rotate that parent then the rest of the cubies will rotate with it , knowing this fact we can proceed and create controls to manage cubies parenting.

Cube face rotation You can take a look at the rotation() function , inside it we do the whole cube rotation operation as well face rotation , for face rotation we first get the selected face to be rotated via the button or keyboard input , then we use it as a map to our 2D cube’s face where it will return to us the previously mentioned method of returning an array of tags for that specific face.

We then proceed to change the parent of the tagged cubies to the selected face , next we rotate the 2D cube (we also rotate it an additional two times if reverse was selected, a little hack but has no visual effect , what comes around goes around :D ).

Finally we select the rotation speed from the drop down list combo box and flag start the rotation as well play the sfx sound of rotation , when that happens , the center piece of the selected face will rotate 90 degrees in factors of 90 speed until the count expires (I’m using factors of 90 to represent the rotation speed as floating point numbers due their nature are prune to precision and marginal errors that can accumulate and cause rotation precision bugs which can make the cube glitchy), then the rotation is complete and variables as well parents are reset for the next rotation.

Whole cube rotation We simply parent the whole cube pieces to one parent which is Cube , it is where we also attach the script , with this we can control the cube as a whole and at the rotation part we can use the mouse right click drag as an input to rotate the whole cube so other faces are revealed , a code snippet as follows:

IV. Cube Scrambling Algorithm

This is a simple aglorithm that populates an arraylist with random faces to be rotated in random directions , currently it rotates 30 random faces, here’s a code snippet:

In our update mehod we detect the scramble flag and proceed to flush the next scrambles until over , during this time the player cannot control the cube until it is reset or done scrambling , trails can still be toggled however and rotation speed tweaked along other options.

V. Cube Solving Algorithm

In order to make an AI that solves the cube , there needs to be some data structures to map the cubies and the tiles , this is demonstrated in the Cube2D script , it is where as well the AI solver takes place , the written script simulates real cube cases with pre-written algorithms to tackle each case , for instance by taking a scrambled cube, the following phases are solved one after another (Magic cube style):

  • The White Cross.
  • The Corner pieces of the 1st layer.
  • The Edge pieces of the 2nd layer.
  • The Yellow Cross.
  • Orientation of the Last Layer (OLL).
  • Permutation of the Last Layer (PLL).
Цукерберг рекомендует:  Операторы ветвления и циклы в JAVA. Примеры использования

IV. Trail Rendering

In order to make face rotation a little bit more eye pleasing , a visual effect is added which is simply adding a trail renderer to each corner piece , this can be customized to your own preference or simply disabled using the UI.

VII. Inputs

Below are the inputs mapped under Controls() method along button inputs with descriptions:

Keyboard Keys/Cube Control UI Buttons (The buttons are colored in respect to the center pieces color of each face)

  • U / Up -> Rotate the white face.
  • D / Down -> Rotate the yellow face.
  • R / Right -> Rotate the blue face.
  • L / Left -> Rotate the green face.
  • F / Front -> Rotate the red face
  • B / Back -> Rotate the orange face.
  • S / Solve -> Solves the cube.
  • Left CTRL / Reverse -> Changes face rotation from clockwise to anti-clockwise and vice versa.

UI Buttons


  • Scramble -> Rotates 30 randomly selected faces clockwise or anti-clockwise.
  • Reset -> Resets the cube.
  • Solve -> Solves the cube.

Toggles

  • Trails -> Enable/Disable Trail Renderer.
  • BGM -> Enable/Disable BGM.
  • SFX -> Enable/Disable SFX.

Others

  • Speed -> Dropdown list with rotation speeds using factors of 90.
  • Timer -> Press space bar to start the timer count from 0 , press again to stop counting.
  • Whole Cube Rotation -> Hold right click and drag to rotate the cube in the direction you’re dragging.

This was all there is regarding this cube simulator, the rest such as sound and canvas as well buttons manipulation can be learned from other tutorials as this guide’s objective is to focus on the rubik’s cube logic implementation in Unity.

Rubic’s cube

Думаю все из нас пробовали собирать кубик рубика, вряд ли у кого то это получалось если не знали формул. Вот приложение, которое поможет выучить алгоритм собирания кубика за семь шагов.

Я выучил все формулы ну где то за неделю, затрачивая на это по часу в день.

Инструкция взята с сайта rucube.ru

лично мной замечена ошибка в шестом шаге, две последних формулы

ФПВП’В’ПВФ’ — эту не знаю как правильно

П»Л»НП»Л»В» — эту знаю как правильно будет: П»Л»НП»Л»В»П»Л»НП»Л»В» (два раза в общем)

алгоритм вращения кубика rubiks

В настоящее время я работаю над заданием построить кубик Rubik. Программе не нужен графический интерфейс. Но он должен имитировать кубик 3 X 3 с поведением вращения и предоставить графическое представление куба (я собираюсь с плоской буквенной структурой). Мой код имеет класс для граней, которые создают Face (другой класс), а затем есть класс куба, который содержит методы вращения.

У меня возникли проблемы с созданием/выбором используемого алгоритма, который бы точно имитировал куб и все возможные вращения. Я нашел решение на этом сайте, ссылаясь на документ, предлагающий 7 различных способов сделать это (ссылка ниже). Но какой метод наиболее интуитивно понятен/легко кодируется? И что более важно, что лучше всего соответствовало бы описанному ниже поведению (в псевдокоде)?

У меня возникли проблемы с пониманием того, как я могу использовать какой-либо метод для учета изменений на каждом лице сразу, особенно принимая во внимание поведение вращения лица (в отличие от строк и столбцов).

Этот псевдокод не учитывает изменения лица.

Есть ли лучший/более простой способ сделать это, отличный от метода, предложенного моим псевдокодом? Как мне объяснить изменения лица?

Пример: (лицевая сторона, 1 оборот, по часовой стрелке)

Примечание. Я склоняюсь к вектору 54 элементов, но я не уверен, как его манипулировать.

Кроме того, это мой первый вопрос, поэтому дайте мне знать, если что-то не так (недостаточно информации, слишком много, неправильная тема и т.д.),

Примечание. Это код, с которым я работаю.

Подумайте о том, какая структура данных облегчает вам концептуализацию куба. Различные решения в ссылке, которую вы предоставили, имеют все плюсы и минусы. У вас может быть более сложная структура (т.е. Пятизначное представление), которая занимает меньше памяти и оптимизирует производительность. Но с этим представлением может быть сложно работать, если вы новичок в этой проблеме. На другом конце спектра представлено объектно-ориентированное представление, которое моделирует, как большинство людей будет думать о кубике rubik. Это может быть лучший подход для кого-то нового для кубиков rubik.

После того, как у вас есть структура данных, которая имеет для вас смысл, подумайте о различных операциях, которые могут выполняться на кубе. Вы можете захватить каждый из этих шагов в функции, которая изменяет состояние данных. Если у вас есть настоящий кубик rubik, с которым можно играть, выясните, что все разные повороты и как они меняют значения кубика rubik. Затем попробуйте смоделировать их в своих собственных функциях. т.е. верхний мудрый поворот заставит пять из шести лиц изменить, правильно. Это приведет к тому, что четыре лица получат верхний ряд своего соседа. И это вызовет сдвиг в верхней части лица. Нижняя грань будет неэффективной.

Если вы чувствуете себя переполненными сложностью, попробуйте разбить проблему на меньшую. Возможно, вы можете попытаться написать представление для кубика rubik размером 2 x 2, а не 3 x 3. Как только вы освоите меньшую проблему, вернитесь к большему.


3D Кубик Рубика на Unity 2 Управление Камерой

Автор видео: def1NeX Качество: HD Всего просмотров: 1,486 Добавлено: 04 April 2020 Продолжительность: 9m 1s

Скачать на телефон

Вам так же понравится

3D Кубик Рубика на Unity 3 Свайп Мышкой

3D Кубик Рубика на Unity 1

L GHT NG In Unity

⚡ Как собирать кубик Рубика 3х3 как профи Метод Джессики Фридрих. ��Обучение F2L Ф2Л

UN TY3D 4 способа задать движение объекту

ANDROIDHELPER [UNITY3D and more]

Рисование 2D меню для игры в Unity Урок Llustrator и Создание игр Флатинго

Flatingo. Разработчик игр

Unity 3D \ C \ Анимация вращения \ Кватернионы Объяснение

Скачать Kubik Rubik 3D — Кубик Рубик 3D

JAVA game Kubik Rubik 3D — ява игра Кубик Рубик 3D
Трёхмерный кубик рубик для мобильного телефона!

Управление:
Чтобы повернуть нужную сторону, каснитесь среднего квадратика этой стороны.

Чтобы повернуть весь кубик, проведите пальцем по экрану:
снизу-вверх/сверху-вниз
или
слева-направо/справа-налево.

Жанр: Логические
Графика: 3D
Разрешение: 240×400
Размер: Full Screen (в Полный экран)
Управление: Адаптировано под Touch Screen
Режим просмотра: Портретный режим
Язык: English
Разработчик: Другой

Скачать бесплатно Java-игру Kubik Rubik 3D 240×400 для сенсорных телефонов:

Проверено на: Samsung s5230 | LG KM900 Arena | Java Games 240×400 | 3D JAVA-Games | Full Screen | Touch Screen

Понравилась новость? Хотите сказать спасибо?

— Поделитесь ссылкой на Kubik Rubik 3D — Кубик Рубик 3D с теми, кто нуждается в этой информации.

Кубик Рубика Unity 5

Я разрабатываю игру для Android с помощью Unity 5, игра — кубик Рубика. Я не использую прикосновение, чтобы переместить куб, но кнопки для перемещения строк и столбцов куба, а для него все вращаются, получил еще один набор кнопок. Моя проблема заключается в том, что это второй набор кнопок, которые они должны иметь две функции: вращать весь куб, а другой — на другую кнопку, которая, когда она предварительно изменяет функцию, позволяет выбрать строку или столбец, который вы хотите переместить.

Java — Кубик рубика на Unity

I need the code to study please I like solve Rubik’s Cube. you can send?
My email: 2001vivaldo@gmail.com
I speak Portuguese and English, I do not understand Russian.

I haven’t understood a word of what you said, but I’ve understood everything you did. Thank you Sir, you’re a god for me. Respect!

Привет чувак, можешь скинуть мне этот проект готовый?
Я хочу разобраться в коде. Мой ВК — vk.com/invading
Спасибо.

Чувак, спасибо тебе большое❤️❤️❤️


Урок топчик, но чувак, огромная простьба! Помедленнее, я записываю))

Почему так быстро?

Когда спамишь на все кнопки подряд кубик просто ломается)0))

Funny Humor этот баг пофикшен во 2 или 3 части

привет, очень хороший канал (пришел из группы по Unity), можешь во 2-ом уроке показать, каксделать подобное на андроид?

Unity — кросс-платформенный движок, поэтому никаких отличий не будет. Либо, если ты имеешь в виду управление поворотами граней, то в следующем уроке я это покажу.

Можешь скрипты скинуть)0)0)

Там два скрипта по 100 строчек) Так шо не)00)))

Приятно, что у тебя появилось время)

Это я! Спасибо огромное, жду)

Я просто подумал, что тратить 8 часов на сон глупо, если в это время можно сделать видос) И может я ошибаюсь, но это ты вроде ждал тутор по 3Д раннеру, если да — то держи радостную весть, на некст неделе 1 урок. А если это был не ты, то сделай вид, что ты этого не читал :)

3D Кубик Рубика на Unity #1

إظهار عناصر التحكم بالمشغّل

  • تم نشره في 2020/04/ 1
  • Сегодня мы создадим 3D симулятор Кубика Рубика. В этом уроке мы реализуем построение куба, а также повороты граней.
    ���� Подписаться на канал: bit.ly/defStudiosSubscribe
    ���� Спонсировать нас на Патреоне — bit.ly/defStudiosPatreon

تعليقات • 23

Eres asombroso, muchas gracias por compartir tu conocimiento

подскажи как правильно задать количество кубиков на грани? допустим если делать 5х5х5

@def1NeX, что ж, очень жаль.
просто хочу сделать очень большой куб. фишкуб на 50х50 например))

Там по-другому логику нужно разрабатывать

I need the code to study please I like solve Rubik’s Cube. you can send?
My email: 2001vivaldo@gmail.com
I speak Portuguese and English, I do not understand Russian.

I haven’t understood a word of what you said, but I’ve understood everything you did. Thank you Sir, you’re a god for me. Respect!

Привет чувак, можешь скинуть мне этот проект готовый?
Я хочу разобраться в коде. Мой ВК — vk.com/invading
Спасибо.

Чувак, спасибо тебе большое❤️❤️❤️

Урок топчик, но чувак, огромная простьба! Помедленнее, я записываю))

Почему так быстро?

Когда спамишь на все кнопки подряд кубик просто ломается)0))


Funny Humor этот баг пофикшен во 2 или 3 части

привет, очень хороший канал (пришел из группы по Unity), можешь во 2-ом уроке показать, каксделать подобное на андроид?

Unity — кросс-платформенный движок, поэтому никаких отличий не будет. Либо, если ты имеешь в виду управление поворотами граней, то в следующем уроке я это покажу.

Можешь скрипты скинуть)0)0)

Там два скрипта по 100 строчек) Так шо не)00)))

Приятно, что у тебя появилось время)

Цукерберг рекомендует:  Вакансии ООО Иммерсмед

Это я! Спасибо огромное, жду)

Я просто подумал, что тратить 8 часов на сон глупо, если в это время можно сделать видос) И может я ошибаюсь, но это ты вроде ждал тутор по 3Д раннеру, если да — то держи радостную весть, на некст неделе 1 урок. А если это был не ты, то сделай вид, что ты этого не читал :)

Я еще не успел прошлый видос освоить и код написать а тут уже второй видос)))))

Пока есть вдохновение решил сделать, а то когда по три месяца нет видосов на канале — это грустно

Пишем эмулятор Кубика Рубика

OpenGL — платформонезависимая спецификация, описывающая программный интерфейс для создания компьютерных приложений, использующих двухмерную и трехмерную графику.
В этой статье я опишу, как можно создать эмулятор Кубика Рубика на OpenGL.

Кубик будет в 3D и его можно будет вращать мышкой, а переворачивать грани можно, кликая мышкой по стрелкам. При том стрелки появляются у ближайшей к зрителю грани.

Я буду описывать создание эмулятора Кубик Рубика на языке C#, для OpenGL буду использовать библиотеку OpenTK. Надо её скачать, и сделать в Visual Studio ссылку на эту библиотеку.

Экскурс в 3D

Теперь небольшое описание про 3D. Объекты в 3D у нас имеют 3 координаты x, y, z, а на экране монитора только две координаты. Очевидно, что на экране монитора надо показывать проекцию.

Но задние предметы или которые стоят сбоку мы не должны проецировать. Также мы не должны проецировать предменты, которые стоят слишком далеко. (Вспомните, как в гонках, далекие предметы появляются когда к ним начинаешь подъезжать).

Поэтому надо ограничить то, что мы можем видеть:

Такая усеченная пирамида называется Фруструм (FrustRoom), чтобы показать предмет на экране, мы определяем помещается ли он в Фруструме (те части, которые не помещаются мы отсекаем), потом мы проецируем на экран. Всё это за нас делает OpenGL.

Проба пера

Скачиваем библиотеку OpenTK. Запускаем файл, распаковываем библиотеку.

Создаём проект, добавляем ссылку на файл OpenTK.dll. А так как, мы будем использовать контрол GLControl, на котором будет отображаться Кубик Рубика, добавляем ещё и ссылку на OpenTK.GLControl.dll
OpenTK требует также ссылку на System.Drawing.dll, поэтому ещё раз входим в интерфейс добавления ссылки, и выбираем вкладочку .Net и ищем System.Drawing, и добавляем.

Я буду использовать OpenGL, внутри обычной GUI-программы. Поэтому в режиме конструктора кликаем по правой кнопкой мыши по панели инструментов, и выбираем “Выбрать элементы”, переходим на вкладку “Компоненты .NET Framework” и выбираем файл OpenTK.GLControl.dll. В списке появится новый элемент GLControl, ставим напротив него галочку. ОК. На панели инструментов появится новый элемент GLControl. Переносим его на форму и растягиваем на всю её форму.

Элемент GLControl имеет событие Load, оно срабатывает, когда этот элемент загрузился.
(Щелкнем по нему, чтобы заполнить тело обработчика, появится метод glControl1_Load)
Создатели OpenTK не рекомендуют начинать работать с GLControl, пока он не загрузился, поэтому нужно заводить переменную, которая будет хранить значение, загрузился ли GLControl:

glControl1_Load — метод, который обрабатывает событие Load
glControl1_Paint — метод, который обрабатывает событие Paint, срабатывает, например, когда мы скрываем, а потом снова открываем окно или, например, изменяем размеры окна.

Собственно нарисуем кубик.

using OpenTK; — нужен для класса Matrix4 (матрица 4×4)
using OpenTK.Graphics.OpenGL; — нужен для получения доступа к объекту GL.

GL — объект, через который собственно вызывать команды OpenGL.
GL.ClearColor(Color.SkyBlue); — заливает голубым цветом
GL.Enable(EnableCap.DepthTest); — эта строчка нужна, чтобы дальние элементы перекрывались ближними.

Здесь мы задаём матрицу, которая отвечает за Фруструм:
1) угол обзора 80 градусов
2) отношение длины к высоте — 1
3) расстояние до первой грани — 20
4) расстояние до дальней грани — 500


Переходим в режим проекции, и задаем эту матрицу. О режимах будет сказано чуть позже.

Инициализируем ColorBufferBit и DepthBuffer

ColorBuffer. Буфер Цвета. С каждым пикселем на экране связано значение цвета, которое записывается в буфере цвета. Вызов GL.Clear(ClearBufferMask.ColorBufferBit) зальет в нашем случае окно цветом SkyBlue (смотри выше).

DepthBuffer. Он же Z-Buffer. Буфер глубины. Дело в том, что две точки в 3D пространстве могут проецироваться на одну точку на экране. Нужно чтобы, ближняя точка перекрывала дальнюю. Для этого нужно вычислять “глубину” точки (величины обратнопорциональной расстоянию от камеры до точки) и записывать её значение в буффер (пискель такой-то, глубина такая-то),
если же очередная точка проецируется на тот же пиксель, то надо сравнивать “глубину” новой точки с записанной Depth-буффере. Если новая точка находится “менее глубоко” (более ближе к камере), то её проекция должна перекрыть существующую, иначе оставляем всё как есть.
В начале отрисовки кубика мы должны очистить Depth-Buffer.

Здесь мы задаем нашу камеру в точке (30, 70, 80), направление взгляда в центр системы координта (0, 0, 0). Ориентация такая, что ось OY направлена вверх.

Если мы сделаем

То мы будем смотреть на кубик под углом, как если бы наклонили голову на 45 градусов влево.

Далее собственно рисуются сам кубик: сначала грани красным цветом, потом черным — ребра

Потом вызывается команда

Дело в том, что по умолчанию OpenGL в OpenTK double-buffer: каждый буффер (ColorBuffer, DepthBuffer и другие, которые я не упомянул) дублируется. Когда мы рисуем изображение, мы используем одни буферы. А в это время на экране отображается изображение, которое получено из других буферов.
Командой glControl1.SwapBuffers(); мы выводим на экран изображение, используя буферы, в которых мы его рисовали.
Кстати, если очисть буфер цвета только в первый раз

То есть очистить только один буффер цвета (на самом деле, залить его голубым цветом), а другой не очищать. А потом сворачивать/разворачивать окно. То цвет фона будет меняться с голубого на черный. (правда, если изменить размеры окна, то станет всегда черный цвет (видимо, оба буффера сбрасываются при ресайзе).

Теперь о режимах

Объекты задаются в 3-х мерных координатах. Эти координаты называются объектными. Каждый объект может быть определен в своих объектных координатах. Чтобы построить мир из разных 3d объектов, которые стоят относительно друг друга в разных положениях,
нужно объектные координаты каждого объекта умножить на соответствующую модельную матрицу (model Matrix). Тогда мы получим новые координаты каждого объекта в новом общем мировом пространстве.

В то же время мы можем смотреть на мир объектов с разных сторон, мы можем перевернуть камеру, мы можем приближаться к объекту и удаляться от него. Умножая координаты объектов (координаты в мировом пространстве) на соответствующие матрицы видового преобразования (view Matrix), мы получаем видовые коордианты каждого объекта.

В OpenGL матрица модельного преобразования (model Matrix) совмещена с матрицей видового преобразования (view Matrix) в одну (modelView Matrix). (Ведь мы можем отдалить объект двумя способами: изменить его мировые координаты (отдалить сам объект), либо отдалить от него камеру (получить новые видовые координаты)).

Потом координаты умножаются на матрицу проекции (projection Matrix), которая либо задаёт Фруструм (перспективное проецирование):

либо задаёт ортогональное проецирование:

Умножая видовые координаты на матрицу проекции, мы получаем усеченные координаты (clip coordinates). Деля каждую координату (x, y, z) на 4 величину ω, мы получаем нормализованые координаты устройства (Normalize Device Coordinates, NDC) каждая из которых от -1 до 1, при том ось Z развернута уже от нас (то есть Фруструм по сути превращается в куб и разворачивается от нас на 180 градусов),
далее координаты сдвигом и масштабированием преобразуются в оконные координаты (window coordinates), которые уже наконец и участвую в построении 2D-изображения на экране.

Чтобы перейти в режим управления матрицей проецирования мы должны вызвать функцию GL.MatrixMode с параметром MatrixMode.Projection:
GL.MatrixMode(MatrixMode.Projection);

А чтобы перейти в режим управления матрицей модельно-видового преобразования мы должны вызвать функцию GL.MatrixMode с параметром MatrixMode.Modelvew:
GL.MatrixMode(MatrixMode.ModelView);

Добавьте в glControl1_Paint код, рисующий оси OX, OY, OZ:

Также в дизайнере форм, надо добавить обработчик для события KeyDown, появится функция glControl1_KeyDown. Заполните её следующим кодом:

То есть при нажатии клавиши A на клавиатуре мы переходим в режим проекции и делаем поворот вокруг оси OZ на 30 градусов против часовой стрелки,
а при нажатии клавиши B тоже осуществляется поворот вокруг оси OZ, но уже в режиме модельно-видового преобразования.

Полный код привожу здесь:

Если мы будем нажимать букву A на клавиатуре, то у нас будет вращаться 2D-изображение на экране:

Таким образом в перспективных координатах ось OZ является и осью Фруструма.

В случае нажатия B на клавиатуре, то у нас будет вращаться система координат вокруг оси OZ:

С тем же успехом можно было заменить таким:

Тот же код над матрицей ModelView даст тот же результат.


Собственно перейдём к описанию программы эмулятора кубика-рубика, которую вы можете скачать здесь: http://труъкодинг.рф/files/opengl.zip

Наконец-то

Всё описывать не буду, так как будет очень много текста. Опишу ключевые моменты.

Ключевые структуры данных

1) Кубик Рубика, ребро которого состоит из 3-х кубиков состоит из 27 маленьких кубиков.
В процессе вращения граней Кубика Рубика (КР), маленькие кубики будут менять своё местоположение. В процессе вращения грани КР, надо знать какие маленькие кубики вращать (ведь в грани могут оказаться разные кубики), также после очередного поворота грани КР, надо проверить, а не собрался ли кубик.

Для отслеживания позиций кубиков, я применил массив positions:
int[] positions;

Его ключи — это номера кубиков, а значения номера позиций.

Кстати, позиции я обозначил так:

2) Когда вращаешь грань КР, соответствующие маленькие кубики меняют не только местоположение, но поворачиваются другими сторонами. Когда мы повернули грань на один оборот (90 градусов),
то новое состояние кубика можно получить двумя путями:
1) повернуть соответствующие кубики вокруг определенной оси на 90 градусов (что и делалалось при повороте)
2) либо переставить кубики, на новые места, и повернуть каждый кубик вокруг своей оси на 90 градусов.

Следующий класс служет для описания кубика в пространстве.

поля X, Y, Z — это углы относительно осей OX, OY, OZ
когда мы вращаем какую-либо грань, у соответствующих кубиков меняется соответствующий угол.
После заканчивания вращения, я обнуляю эти углы, перемещаю кубики на новые позиции (то есть соответствующим образом меняю массив position), и вращаю кубики вокруг своей оси (покажу, что под этим имею в виду). Пользователь видит только само вращение.

Цукерберг рекомендует:  Распознавание текста из изображений через командную строку

Объект класса angleXYZ есть у каждого кубика и хранится в коллекции angles:

3) Каждый кубик содержит 8 угловых точек. Зная эти точки, нарисовать кубик не проблема.
Координаты точек хранятся в 3-х мерном массиве edges. Чтобы перенос и поворот координат проходили с помощью только операций умножения (а не сложение и умножение), я использую матрицы 1×4 для координат и матрицы 4×4 для матриц переноса и умножения.
Использование матриц 4×4 позволяет соединить операцией умножения воедино и матрицу переноса, и поворота. Тем самым за одну операцию умножения можно сделать две вещи: и перенос, и умножение.

Чтобы узнать смещение очередного маленького кубика (из которых состоит Кубик Рубика) относительно нулевого положения в собранном КР, я написал специальную функцию getOffsets, которая принимает номер кубика и возвращает на сколько кубиков надо отступить по каждой из осей.

4) Есть ещё словарь intersect_planes.
Ключи словаря — это оси (объект перечисления Axis (public enum Axis < X, Y, Z >;)),
а значения — это грани на соответствующей оси, объекты моего класса Plane (плоскость).

Класс Plane нужен для хранения координат точек угловых точек каждой грани.

объект класса Plane просто хранит координаты 3-х точек, а конструктор этого класса проверяет, чтобы они были разные. Но можно было и не заводить отдельный класс, а просто обойтись двумерным массивом:

но напрягает обращаться к элементам массива через череду квадратных скобок.

Этот словарь нужен для того, чтобы определить по плоскости какой грани мы щелкнули мышкой, соответственно какую часть Кубика Рубика надо вращать. Об определении плоскости, по стрелкам которой щелкнули мышкой, будет написано позже.

5) Важный объект моего vp класса ViewPoint.

который хранит значение координаты точки обзора Кубика Рубика, дело в том, что при вращении мышкой Кубика Рубика, у меня на самом деле меняется положение точки обзора, а кубик стоит на месте.

Класс ViewPoint нужен для получения ближайшей оси к точке зрения (метод getNearestAxis). Это нужно, чтобы определить, на какой грани показывать стрелочки, то есть какие части вращать, при щелчке мыши.

Точка обзора вращается вокруг Кубика Рубика по сфере, поэтому удобно оперировать углом относительно оси OX (угол α) и углом относительно ocи OY (угол β):

У объекта vp открыты свойства-сеттеры angle_view_alpha и angle_view_beta, через них меняются угол α и угол β, а в теле сеттеров по этим углам рассчитываются координаты камеры (точки обзора).
Также у этого класса есть свойства-геттеры, по которым можно определить не верх ногами находится ли камера, с какой стороны определенной оси мы смотрим на кубик (к примеру со стороны положительных значений X, или со стороны отрицательных значений Z).
Это нужно для того, чтобы правильно определять в какие стороны крутить грани Кубика Рубика. Сам Кубик Рубика расположен так, чтобы его центр был в центре начала координат.

Перейдём к коду

Я буду описывать только ключевые моменты, иначе будет очень долго. Я и так себя уже Львом Николаевичем чувствую.

Метод Render

Сам Кубик Рубика я хочу, нарисовать так, чтобы его центр совпадал с центром начала координат, поэтому перейдя в режим матрицы ModelView, я делаю перенос системы координат на половину длины Кубика Рубика по всем осям:

double offset0 = this.w * N + (N — 1) * spacing;//длина Кубика Рубика w — длина маленькоо кубика, N — размерность КР (3), spacing — расстояние между маленькими кубиками.
double offset = offset0 / 2;
GL.Translate(
-offset,
-offset,
-offset
);


Потом по количеству кубиков (27 раз) вызывается функция cube, которая рисует маленькие кубики за исключением того, что находится в центре КР, потому что его не будет видно никогда.

Функция cube

Я сначала сохраняю, текущую матрицу ModelView в стэке, который предоставляет OpenGL:

а в конце восстанавливаю эту матрицу из стэка:

Это нужно, чтобы изменение матрицы ModelView (повороты, переносы) одного кубика не влияли на матрицы других кубиков. Другими словами, если мы хотим крутить один маленький кубик, то не должны при этом крутиться другие.

Для анимации прокрутки грани, я делаю поворот вокруг вокруг какой-либо из осей.

Код написан так, что только один из углов angle.X, angle.Y, angle.Z в один момент може быть ненулевым, так что здесь выполняется поворот только вокруг одной оси, либо не выполняется вообще.

Но, памятуя о том, что система координат смещена, надо сначала вернуть её на место, сделать поворот, и снова сделать обратный перенос, что у меня и сделано:

Далее используется массив edges для рисования кубика, цвета граней кубика определяются из номера кубика, который передаётся функции cube.

Стрелки

Центр системы координта с помощью GL.Translate возвращается в первоначальное место.
Потом с помощью объекта vp класса ViewPoint определяется наиближайшая к камере ось системы координат и с какой стороны. Делается соответствующий поворот с помощью GL.Rotate и методом GL.Translate делается такой перенос, чтобы стрелки рисовались у ближайшей грани.

Чтобы определить по какой стрелке мы щёлкнули, сначала надо определить прямую идущую от места щелчка мыши, а потом надо определить место пересечения с ближайшей плоскостью, содержащей грань кубика, далее зная размеры маленьких кубиков и расстояния между ними, не составит труда определить по какой стрелке мы щёкнули.

Для определения 2-х точек такой прямой находятся нормализованные координаты устройства (NDC, речь о них выше), которые надо умножить на инвертированную матрицу, полученную в помощью умножения матриц ModelView и проекции.
В результате мы получим координаты двух точек, лежащих на ближайшей и дальней плоскостях Фруструма.

Снова возвращаемся в метод Render

Зная ближаюшую грань и две точки прямой, мы находим точку пересечения прямой и плоскости, где лежит ближайшая к нам грань Кубика Рубика (то есть в дело вступает словарь intersect_planes, содержащий точки плоскости).

Функцию определения определения точки пересечения я нагуглил. Далее в коде определяется в грань мы кликнули или в стороне от неё (по стрелке), и с боку от грани или сверху снизу. Это всё легко потому, что сам кубик не двигается, а его стороны паралельны плоскостям XOY, XOZ, YOZ (то есть всё определяется сравнением меньше/больше координат точки пересечения и координат 4-х угловых точек).
Ну а потом запускаем процесс вращения грани.

Вращение грани

Для анимации вращения грани я создал класс EasingTimer, наследуемый от System.Timers.Timer, с помощью шаблона проектирования Singleton, можно создать только один элемент этого класса. Это я сделал для того, чтобы случайно две грани одновременно не вращались. Также есть переменная run, которая определяет запущен ли процесс вращения, и новый процесс вращения не наступит, пока не закончится предыдущий.

Свойство duration объекта класса EasingTimer задаёт в миллисекундах длительность вращения.
Само вращение происходит так:

  1. сохраняется начальное время
  2. стартуется Таймер на один раз на 100 миллисекунд.
  3. когда срабатывает таймер, вызывается функция поворота грани rotatePart, которая изменяет элементы списка angles соответствующих кубиков. Напомню, что каждый элемент этого списка отвечает за поворот определенного кубика в пространстве.

функция поворота, изменяет только один угол либо X, либо Y, либо Z,
таймер необязательно сработает через 100 миллисекунд, поэтому очередная дельта угла поворота определяется исходя из миллисекунд, прошедших от даты, которую мы сохранили на первом шаге.
Таймер перезапускается, и вызывается метод glControl1.Invalidate();, который вызвыает метод Render, который в свою очередь вызывает метод cube для отрисовки каждого маленького кубика, а так как у некоторых кубиков соответствующие элементы списка angles изменены, то мы увидим вращение грани. Грань будет продолжать вращаться, пока срабатывает таймер.

  • Когда функция поворота грани rotatePart, определяет что прошло duration миллисекунд.
    она сбрасывает углы поворота у кубиков, которые поворачивала (если в этот момент вызывать метод Render, то Кубик Рубика будут в предыдущем состоянии, как будто ничего не вращали).
    Далее меняется массив positions (который указывает, где какой кубик находится, смотри выше), чтобы отображать новое состояние Кубика Рубика. Но этого не достаточно — после вращения грани, соответсвующие маленькие кубики не только поменяли своё местоположение, но и словно сделали поворот вокруг своей оси.
    Другими словами поворот маленького кубика при вращении грани, эквивалентен изменению положения кубика и его вращение вокруг своей оси:
  • Поэтому элементы массив edges, содержащим координаты 8 точек маленьких кубиков, которые вращались умножаются на соответствующую матрицу поворота.
    Далее опять через вызов glControl1.Invalidate(); вызывается метод Render, который будет вызывать метод cube, которая покажет кубики на новых местах и соответствующе повернутых.

    Теперь разъясню почему был придуман массив positions, указывающий на какой позиции какой кубик стоит (27 кубиков, 27 позиций). Дело в том, что в процессе вращения граней Кубика Рубика, маленькие кубики будут всё время менять своё местоположение, и надо уже оперировать не командами вида “повернуть кубик номер 3 на 90 градусов вокруг оси X”, а оперерировать командами вида “повернуть кубик, стоящий на позиции номер 3, на 90 градусов вокруг оси X”.

    Теперь разъясню, почему я сбрасываю в нули элементы массива angles, отвечающих за поворот кубика в пространстве. Дело в том, что последовательность поворотов имеет значение. Поворот вокруг оси X, потом поворот вокруг оси Y не тоже самое, что поворот вокруг оси Y, а потом поворот вокруг оси X.

    Если никогда не обнулять углы поворота кубиков, то следующий код из метода cube, реализующий анимацию вращения

    будет нормально работать только первые три вращения, да и то, если будут в таком порядке: вокруг X, вокруг Y, вокруг Z.

    Нужно запоминать последовательность вращения каждого кубика, некий трэйс поворотов нужно иметь. Массив edges содержит координаты угловых точек маленьких кубиков, каждый раз умножая координаты кубика, на соответствующую матрицу поворота, мы сохраняем историю всех поворотов этого кубика. Нам осталось только прочитать элемент массива edges и вывести на экран соответствующий кубик.

    Вращение всего Кубика Рубика

    Вращение всего Кубика Рубика происходит только с помощью изменения точки обзора. Это реализовано в обработчиках MouseDown, MouseMove, MouseUp. Просто высчитывается, на сколько переместилась мышка по окну программы, перемещения преобразуются в соответствующие углы поворота, по вертикали и горизонтали.

    Изменяются соответствующие свойства объекта vp класса ViewPoint, отвечающие за углы от оси X, и Y (смотри картинку выше), а внутри методов класса ViewPoint определяется новые координаты камеры и перевернута ли она, затем создаётся новая матрица ModelView:

    Понравилась статья? Поделиться с друзьями:
    Все языки программирования для начинающих