Добавлен girvel,
опубликован
Галактика сплющена по вертикали, поскольку я подключил второй монитор и не настроил графический планшет на правильное взаимодействие с ним. В результате он стал воспринимать в два раза большую по ширине рабочую область при такой же ее высоте. Чувствительность по вертикали стала меньше, чем по горизонтали. Пока до меня это дошло, галактику я уже дорисовал.
Прошло уже больше недели с тех пор, как я создал проект на XGM, и я подумал, что можно было бы рассказать в общих чертах о том, чем я тут занимаюсь. Здесь будет довольно много кода и совсем чуть-чуть общих слов о прогрессе разработки в конце поста.
Меня зовут гирвел и я трачу слишком много времени на код
Когда проект разрабатывает художник, ему всегда будет, что показывать - результат его разработки обычно выглядит вполне себе опрятно и внешне очень похож на игру. У меня же результат выглядит как-то так:
Это, к слову, должна была быть ракета со спейс марином в ней.
Между тем, большую часть времени я трачу на написание кода. Поэтому мне хотелось бы немного рассказать о внутренностях игры и совсем чуть-чуть поныть о том, как я мучился с документацией юнити. Все это будет происходить на поучительном примере написания системы SpacePhysics, состоящей из всего двух классов.
Кстати, пишу я на C#, потому что скрипты для Unity все пишут на C#. А еще потому, что я могу только в C#.
SpacePhysics - система, которая имитирует космическую физику. Работает она на основе Rigidbody2D (стандартная система Unity, делающая из двумерного объекта твердый двумерный объект) и позволяет выводить объекты на орбиты друг друга и заставлять их кружиться по красивым траекториям.
То, что при написании любого объектно-ориентированного кода надо делать в первую очередь - рисовать UML-диаграмму и заранее планировать, как все будет работать. У SpacePhysics окончательная диаграмма выглядит так:
UML диаграмма что это
UML диаграмма - диаграмма, отображающая структуру кода.
Существует некий стандарт UML, можно записывать в диаграммы всю информацию подряд и проводить время в прочих бессмысленных перфекционистских занятиях, но я предпочитаю делать небольшие диаграммы с короткими пояснениями, где указываются только публичные члены для экономии времени.
Между тем, это тот вариант, который был достигнут методом проб и ошибок за три дня и к которому я пришел, перепробовав несколько вариантов реализации. Мораль такова - стройте диаграммы и планируйте код заранее, пусть C# в сочетании с Visual Studio и позволяет переписывать огромные куски кода без проблем. А еще читайте документацию, пусть она большей частью и не русская, но если она не про Quaternion, то понять можно, а идея переписывать все системы самому не такая хорошая.
Так вот, есть класс GlobalPhysicsController, который производит все расчеты, а также содержит гравитационную константу G, которая и близко не соответствует G в реальном мире во славу геймплея. Еще он имеет список всех объектов, подверженных космической физике, и с определенной частотой в функции события FixedUpdate() прикладывает к ним силу, рассчитываемую по формуле ниже, с помощью метода <Rigidbody2D>.AddForce()
F = (G * m1 * m2) / dist ^ 2
Он наследует из MonoBehaviour'а и прикрепляется к объекту космоса, который существует только ради GlobalPhysicsController'а и ничего другого не делает.
Таким образом, общая идея:
class GlobalPhysicsController : MonoBehaviour
{
public const G = 3e-3f;
protected static List<GameObject> spaceObjects;
protected virtual void FixedUpdate()
{
// code
}
}
FixedUpdate() является встроенной функцией события, поэтому всегда является protected virtual, чтобы можно было написать класс-наследник и перегрузить функцию события.
Второй класс - LocalPhysicsController. Он содержит информацию о массе объекта (mass), которая может быть отрицательной (поскольку в дальнейшем это будет использовано как геймплейная фича), которая постоянно конфликтует с массой Rigidbody2D. Я слишком ленивый, чтобы искать решение этой проблемы.
Также класс содержит информацию о объектах, находящихся на орбите владельца LocalPhysicsContoller'а, и объекте, на орбите которого находится он сам. Этого нет в диаграмме, они появились уже после ее составления и редактировать ее я мне опять же лень.
И еще есть метод Orbite(), который выводит объект-владелец на орбиту любого другого объекта, но обычно это делается прямо из Unity.
Прикрепляется к любому объекту, делая его восприимчивым к космической физике, опять же через наследование из MonoBehaviour
class LocalPhysicsController : MonoBehaviour
{
public float mass;
protected GameObject orbiteParent;
protected List<GameObject> orbiteChildren;
public void Orbite(gameObject orbiteParent)
{
// code
}
}
Если кому то интересно, вот код обоих классов.
код
GlobalPhysicsController
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Systems.SpacePhysicsSystem
{
[AddComponentMenu("Physics 2D/Global Phycics Controller")]
public class GlobalPhysicsController : MonoBehaviour
{
// cache:
static List<Rigidbody2D> soRigidbodies2D = new List<Rigidbody2D>();
static List<Transform> soTransforms = new List<Transform>();
static List<LocalPhysicsController> soPhysics = new List<LocalPhysicsController>();
// constants:
/// <summary>
/// Gravitational constant
/// </summary>
public const float G = 1e-3f;
// fields:
public static List<GameObject> spaceObjects = new List<GameObject>();
public static void AddSpaceObject(GameObject spaceObject)
{
spaceObjects.Add(spaceObject);
soRigidbodies2D.Add(spaceObject.GetComponent<Rigidbody2D>());
soTransforms.Add(spaceObject.GetComponent<Transform>());
soPhysics.Add(spaceObject.GetComponent<LocalPhysicsController>());
}
public static void RemoveSpaceObject(GameObject spaceObject)
{
spaceObjects.Remove(spaceObject);
soRigidbodies2D.Remove(spaceObject.GetComponent<Rigidbody2D>());
soTransforms.Remove(spaceObject.GetComponent<Transform>());
soPhysics.Remove(spaceObject.GetComponent<LocalPhysicsController>());
}
void FixedUpdate()
{
for (int i2 = 0; i2 < soTransforms.Count; i2++)
{
for (int i = 0; i < soTransforms.Count; i++)
{
if (soTransforms[i].position == soTransforms[i2].position)
continue;
var v = soTransforms[i].position - soTransforms[i2].position;
float l = v.magnitude;
v.Normalize();
v *= G * soPhysics[i].mass * soPhysics[i2].mass / Mathf.Pow(l, 2);
soRigidbodies2D[i2].AddForce(v, ForceMode2D.Force);
}
}
}
}
}
LocalPhysicsController
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Systems.SpacePhysicsSystem
{
[AddComponentMenu("Physics 2D/Local Phycics Controller")]
public class LocalPhysicsController : MonoBehaviour
{
// cache:
Rigidbody2D thisRigidbody2D;
Transform thisTransform;
// fields:
public float mass;
public GameObject orbiteParent;
public List<GameObject> orbiteChildren;
public void Orbite(GameObject orbiteParent, bool calledByParent = false)
{
if (!calledByParent)
{
this.orbiteParent = orbiteParent;
}
orbiteParent.GetComponent<LocalPhysicsController>().orbiteChildren.Add(gameObject);
var d = orbiteParent.transform.position - thisTransform.position;
var v = Mathf.Sqrt(GlobalPhysicsController.G * orbiteParent.GetComponent<LocalPhysicsController>().mass / d.magnitude);
var v2 = orbiteParent.GetComponent<Rigidbody2D>().velocity + new Vector2(d.normalized.y * v, -d.normalized.x * v);
thisRigidbody2D.velocity += v2;
foreach (var c in orbiteChildren)
c.GetComponent<LocalPhysicsController>().Orbite(orbiteParent, true);
}
public void Orbite()
{
Orbite(orbiteParent);
}
void Start()
{
if (orbiteParent != null)
Orbite();
thisRigidbody2D = GetComponent<Rigidbody2D>();
thisTransform = GetComponent<Transform>();
thisRigidbody2D.gravityScale = 0;
orbiteChildren = new List<GameObject>();
GlobalPhysicsController.AddSpaceObject(gameObject);
}
void Destroy()
{
GlobalPhysicsController.RemoveSpaceObject(gameObject);
}
}
}
Также немного о проделанной работе
- Написана собственная простенькая система анимации, более удобная и логичная
- Можно очень удобно отслеживать действия игрока (нажатия кнопок, мышь, ...)
- Написана система юнитов
- Персонаж уверенно передвигается в космосе, стреляет с анимацией по стенам сгустками плазмы, которые от них рикошетят и, если сильно постараться, с десятой попытки убивают игрока
- Ракету на больших скоростях сильно трясет. Это вышло случайно, но выглядит очень реалистично.
Если вас заинтересовал проект, можете подписаться на проект в соцсетях:
Я не знаю, насколько интересной и понятной получилась статья, поэтому буду ряд любому фидбеку. Мне очень важно знать, насколько интересным получился лог, что вышло занудно, где лучше было бы быть посерьезнее.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.
Отредактирован girvel
Отредактирован SomeFire