Skip to content
Alexey edited this page May 4, 2020 · 19 revisions

Актуально (28.09.2019)

Component

Компонент - это кирпич игры описывающий некий набор переменных. Наборы компонентов формируют контекст сущности и хранят ее состояние. Во фреймворке компоненты часто пишутся с атрибутом [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
}

Код сниппета для Visual Studio

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

Image from Gyazo

Как обращаться к компонентам

Проведу аналогию с 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))
{
				 
}

Как создавать без региона Helper (Advanced)

Если мы не хотим использовать хелперы в классах компонентов, то можем воспользоваться аттрибутом:

     [ActorsComponent]

и создавать обычные классы без сниппетов, указав атрибут класса:

using Pixeye.Actors;
 using UnityEngine;
 
 namespace Components
 {
     [ActorsComponent]
     public class ComponentClick
     {
         public string Id;
         public Vector2 Point;
     }
 }

при этом определении компонента есть особенностей:

1. быстрый доступ к компоненту через метод

var cClick = entity.ComponentClick()// вызовет ошибку компилляции

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

var cClick = Storage<ComponentClick>.components[entity.id]; 

2. назначение компонента сущности

автоматический конструктор через Set работать не будет

var click = entity.Set<ComponentClick>(); // вызовет null ref при выполнении
click.Id = id
click.Point = point;

используйте конструктор и добавление раздельно

var click = new ComponentClick()
{
	Id = id,
	Point = point
};
entity.Set(click);