Добавлен 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