-
Notifications
You must be signed in to change notification settings - Fork 76
(RU) Components
Актуально (28.09.2019)
Компонент - это кирпич игры описывающий некий набор переменных. Наборы компонентов формируют контекст сущности и хранят ее состояние. Во фреймворке компоненты часто пишутся с атрибутом [Serializable] для того чтобы редактировать поля в инспекторе Unity.
Помимо самого класса компонента данных обязательно нужно объявить класс хранилища компонентов (зона хелперов в примере).
Начинайте имя компонента с
Component
, в противном случае ломается debug-режим фреймворка (переключить в release можно с помощью пункта меню Actors в Unity)
using System;
using Pixeye.Actors;
namespace Pixeye.Source
{
[Serializable]
public class ComponentHealth
{
public int val;
}
#region HELPERS
public static partial class Component
{
public const string Health = "Pixeye.Source.ComponentHealth";
internal static ref ComponentHealth ComponentHealth(in this ent entity)
=> ref Storage<ComponentHealth>.components[entity.id];
}
sealed class StorageComponentHealth : Storage<ComponentHealth>
{
public override ComponentHealth Create() => new ComponentHealth();
public override void Dispose(indexes disposed)
{
foreach (int id in disposed)
{
ref var component = ref components[id];
component.val = 0;
}
}
}
#endregion
}
Где {FullName} - имя компонента. Самым удобным способом будет создать шаблон внутри вашего IDE для быстрого создания компонентов.
using Pixeye.Actors;
namespace Pixeye.Source
{
public class ${FullName}
{
}
#region HELPERS
public static partial class Component
{
public const string $end = "Pixeye.Source.$FullName";
internal static ref ${FullName} ${FullName}(in this ent entity)
=> ref Storage<${FullName}>.components[entity.id];
}
sealed class Storage${FullName} : Storage<${FullName}>
{
public override ${FullName} Create() => new ${FullName}();
public override void Dispose(indexes disposed)
{
foreach (var id in disposed)
{
ref var component = ref components[id];
}
}
}
#endregion
}
Shortcut для вызова сниппета - comp
. После двойного нажатия TAB вам нужно ввести имя компонента (без префикса Component, он уже прописан), после чего вы можете нажать снова TAB и ввести namespace, в котором находится класс компонента данных (или отредактируйте сниппет, исключив это).
<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>Класс компонента данных</Title>
<Shortcut>comp</Shortcut>
<Description>Шаблон компонента данных</Description>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Imports>
<Import>
<Namespace>Pixeye.Actors</Namespace>
</Import>
</Imports>
<Declarations>
<Literal>
<ID>shortname</ID>
<ToolTip>Имя компонента без суффиксов</ToolTip>
<Default>Name</Default>
</Literal>
<Literal>
<ID>namespace</ID>
<ToolTip>Неймспэйс в котором находится класс компонента данных</ToolTip>
<Default>Namespace</Default>
</Literal>
</Declarations>
<Code Language="csharp">
<![CDATA[
public class Component$shortname$
{
$end$
}
#region HELPERS
public static partial class Component
{
public const string $shortname$ = "$namespace$.Component$shortname$";
internal static ref Component$shortname$ Component$shortname$(in this ent entity)
=> ref Storage<Component$shortname$>.components[entity.id];
}
sealed class Storage$shortname$ : Storage<Component$shortname$>
{
public override Component$shortname$ Create() => new Component$shortname$();
public override void Dispose(indexes disposed)
{
foreach (var id in disposed)
{
ref var component = ref components[id];
//dispose (reset) logic
}
}
}
#endregion
]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
Чтобы использовать сниппет, разместите файл с расширением .snippet по расположению %USERPROFILE%\Documents\Visual Studio 2019\Code Snippets\Visual C#\My Code Snippets.
Руководство по сниппетам. (доп.)
Create->Actors->Add->Component
Проведу аналогию с Unity: раньше мы делали что-то вроде gameobject.GetComponent<{НАЗВАНИЕ}>()
- gameobject
выступал в роли сущности. Теперь gameobject
сам является лишь частью сущности.
Через сущность мы можем попытаться обратиться к любому компоненту:
var entity = 1; // берем первую сущность на сцене
//этот метод-расширение определен в хелпере
var cObject = entity.ComponentObject(); // напрямую берем ComponentObject
Проверить наличие компонента у сущности:
var entity = 1; // берем первую сущность на сцене
if (entity.Has<ComponentObject>()) {} // проверяем наличие
Одновременно достать компонент и проверить его наличие.
var entity = 1; // берем первую сущность на сцене
ComponentObject cObject;
if (entity.Get<ComponentObject>(out cObject)) {
// do stuff with cObject
}
Одновременно достать несколько компонентов и проверить их наличие.
ComponentAnimation cAnimation;
ComponentMotion cMotion;
if (entity.Get(out cAnimation, out cMotion))
{
}
Если мы не хотим использовать хелперы в классах компонентов, то можем воспользоваться аттрибутом:
[ActorsComponent]
и создавать обычные классы без сниппетов, указав атрибут класса:
using Pixeye.Actors;
using UnityEngine;
namespace Components
{
[ActorsComponent]
public class ComponentClick
{
public string Id;
public Vector2 Point;
}
}
в этом случае быстрый доступ к компоненту через метод
var cClick = entity.ComponentClick()// вызовет ошибку компилляции
будет недоступен, для доступа к компоненту используйте другие способы, указанные выше или
var cClick = Storage<ComponentClick>.components[entity.id];