Извините, ничего не найдено.

Не расстраивайся! Лучше выпей чайку!
Регистрация
Справка
Календарь

Вернуться   www.boolean.name > Программирование игр для мобильных телефонов > Monkey

Monkey Разработка игр на движке Monkey

Ответ
 
Опции темы
Старый 17.08.2016, 05:23   #1
Жека
Дэвелопер
 
Регистрация: 04.09.2005
Адрес: Красноярск
Сообщений: 1,374
Написано 489 полезных сообщений
(для 882 пользователей)
MonkeyBehaviour

Всем привет.
Я тут написал простенькую модель проги (фреймворк) по типу юнити,
c классами GameObject, Component, MonkeyBehaviour, Transform, Sprite (и др).

И меня заинтересовал вопрос про оптимизацию LateUpdate - понравился мне этот метод, хоть он нужен довольно редко.
нужна ли оптимизация - не понятно, но в академических целях - почему нет.

Обработка стартует со сцены (Scene), у которой есть коневой GameObject, и далее вглубь по всем дочерним объектам. В каждом объекте - проход по всем компонентам.

Получается, надо прогнать 2 цикла по всему - сначала Update, затем LateUpdate.

Я придумал решение - через рефлексию. Пробежать по всем компонентам, и проверить наличие метода LateUpdate, если переопределён хоть в одном классе, значит ок, будем пробегать второй раз.
Проверку предполагается делать при старте/создании сцены, после юнитевского аналога Start().

Но что, если этот метод есть всего в одном скрипте?
Такое размышление приводит к мысли, что можно хранить все скрипты в едином списке.
Это позволит к тому же легко задать приоритет выполнения простой сортировкой списка, а также избавит от необходимости пробегать по всем дочерним элементам.
Например, делаем два списка - 1й для тех кто содержит Update, 2й для LateUpdate.

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

Но динамически рефлексию не хотелось бы юзать. Да и вообще, сомнительное решение получилось.
(Offline)
 
Ответить с цитированием
Старый 17.08.2016, 06:51   #2
mingw
Нуждающийся
 
Аватар для mingw
 
Регистрация: 01.01.2016
Сообщений: 89
Написано 45 полезных сообщений
(для 84 пользователей)
Ответ: MonkeyBehaviour

А зачем он нужен? Пост-обновление можно реализовать и в самом методе обновления... но если уж так приспичило, то считаю оптимальным вариантом с отдельным списком объектов, которые имеют метод LateUpdate(dt#). В случае с динамическим подходом - то перед перебором всех объектов сцены, очищаем список и во время перебора добавляем к нему объекты нуждающиеся в пост-обработке. После чего, перебираем получившийся список. Ах, да... если тебе важна скорость... то, не стоит генерить линки на лету, достаточно их хранить в объекте, добавляя их в список.
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
St_AnGer (17.08.2016)
Старый 17.08.2016, 08:35   #3
Жека
Дэвелопер
 
Регистрация: 04.09.2005
Адрес: Красноярск
Сообщений: 1,374
Написано 489 полезных сообщений
(для 882 пользователей)
Ответ: MonkeyBehaviour

LateUpdate любого (всех) компонента должен выполняться после всех Update компонентов, так что в самом методе обновления не сделаешь.

Применение - например следящая за объектом камера, которая движется после того как нужный объект подвигался. Аналогично - мобы с ИИ.

Кстати, можно без рефлексии сделать, путём вычленения LateUpdate в отдельный интерфейс.
Тогда можно будет формировать список через проверку на instanceof:
1. Для скриптов с LateUpdate наследуемся через
extends MonkeyBehaviour implements ILateUpdate
2. В GameObject в функции AddComponent делать проверку и добавлять в список (это внутренности, которые программист не видит/не трогает)
Local late := ILateUpdate(component'если null значит не содержит этот интерфейс
If (late <> Null) Then Updater.AddLate(Self, component) 
Аналогично можно раскидать любые методы типа Update, Draw, чтобы сформировать для них списки.
Не нужно рисование - не наследуешь IDraw, и т.п.
Тоже на изврат похоже.
(Offline)
 
Ответить с цитированием
Старый 17.08.2016, 09:15   #4
Жека
Дэвелопер
 
Регистрация: 04.09.2005
Адрес: Красноярск
Сообщений: 1,374
Написано 489 полезных сообщений
(для 882 пользователей)
Ответ: MonkeyBehaviour

Кому интересно взглянуть на код - репозиторий.
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
Randomize (17.08.2016)
Старый 17.08.2016, 09:15   #5
Nex
Гигант индустрии
 
Аватар для Nex
 
Регистрация: 13.09.2008
Сообщений: 2,888
Написано 1,183 полезных сообщений
(для 3,292 пользователей)
Ответ: MonkeyBehaviour

А что если сделать примерно так. Написать свой Behaviour где LastUpdate (и прочие функции) будут определены сразу как virtual и игрок при написании своего скрипта наследуется от Behaviour и переопределяет нужные ему функции. А в самом Behaviour все эти стандартные функции вызываются в любой ситуации.

Вроде в XNA что то подобное.
(Offline)
 
Ответить с цитированием
Старый 17.08.2016, 11:16   #6
Жека
Дэвелопер
 
Регистрация: 04.09.2005
Адрес: Красноярск
Сообщений: 1,374
Написано 489 полезных сообщений
(для 882 пользователей)
Ответ: MonkeyBehaviour

Nex, сейчас так и сделано:
Interface IBehaviour
    Method Awake
:Void()
    
Method Start:Void()
    
Method Update:Void()
    
Method LateUpdate:Void()
    
Method Draw:Void()
End

Class MonkeyBehavior Extends Component Implements IBehaviour Abstract

    
Field enabled:Bool True
    
    Method Awake
:Void()
        
    
End
    Method Start
:Void()
        
    
End
    Method Update
:Void()
        
    
End
    Method LateUpdate
:Void()
        
    
End
    Method Draw
:Void()
        
    
End
End 
То есть мы имеем стандартную реализацию методов - пустышки, чтобы можно было переназначать только то, что требуется.

Вопрос как раз в том, чтобы не вызывать ВСЕ эти методы, а только при наличии переопределённых в коде юзера.
(Offline)
 
Ответить с цитированием
Старый 17.08.2016, 13:48   #7
Nex
Гигант индустрии
 
Аватар для Nex
 
Регистрация: 13.09.2008
Сообщений: 2,888
Написано 1,183 полезных сообщений
(для 3,292 пользователей)
Ответ: MonkeyBehaviour

Тогда как когда то pax рекомендовал делать через рефлексию и проверять используется ли функция в скрипте игрока. Так даже можно делать без virtual/override.
Просто при старте игры проверять код на наличие нужных функций и если есть, то делать из найденных функций делегат, который работает быстро в отличии от рефлексии. А там уже например в лист сохраняешь делегаты и вызываешь.

Я такое пробовал использовать на c# + 2д движек. И даже без делегатов на небольших сценках все быстро работало.

Сообщение от Nex Посмотреть сообщение
Научите, пожалуйста, как делать методы подобно Start() или Update(), которые уже где то в Юнити объявлены, но при этом их можно использовать/не использовать без override.
Я пробовал несколько способов, но не удачно.
1) Интерфейс требует что бы было объявлено и обязательно public.
2) Если объявлено выше по иерархии, то надо перезаписать.
3) Абстрактный метод как и интерфейс - нужно объявлять.
Какие еще способы есть?
Сообщение от pax Посмотреть сообщение
Все просто, это рефлексия
obj.GetType().GetMethod("MyMethod").Invoke(objarguments[]) 
Пара ссылок
https://msdn.microsoft.com/ru-ru/lib...eflection.aspx
https://msdn.microsoft.com/ru-ru/lib...v=vs.110).aspx
https://msdn.microsoft.com/ru-ru/lib...v=vs.110).aspx

Правда в Unity не просто рефлексия, а там несколько сложнее. Но принцип один, в Unity ничего не объявлено, все ищется по по именам.

Вот вам пример:
using UnityEngine;
using System.Reflection;

/// <summary>
/// Аналог SendMessage с более продвинутыми функциями.
/// Посылает сообщения даже выключенным объектам.
/// </summary>
public static class Message
{

    public static 
void InvokeMethod(this GameObject gameObjectstring methodName)
    {
        foreach (
Component c in gameObject.GetComponents<Component>())
        {
            
MethodInfo m c.GetType().GetMethod(methodName);
            if (
!= null)
                
m.Invoke(cnull);
        }
    }

    public static 
void InvokeMethod(this GameObject gameObjectstring methodNameparams object[] parameters)
    {
        foreach (
Component c in gameObject.GetComponents<Component>())
        {
            
MethodInfo m c.GetType().GetMethod(methodName);
            if (
!= null)
                
m.Invoke(cparameters);
        }
    }
    
    public static 
void InvokeMethodUpwards(this GameObject gameObjectstring methodName)
    {
        
Transform t gameObject.transform;
        while (
t)
        {
            foreach (
Component c in t.gameObject.GetComponents<Component>())
            {
                
MethodInfo m c.GetType().GetMethod(methodName);
                if (
!= null)
                    
m.Invoke(cnull);
            }
            
t.parent;
        }
    }

    public static 
void InvokeMethodUpwards(this GameObject gameObjectstring methodNameparams object[] parameters)
    {
        
Transform t gameObject.transform;
        while (
t)
        {
            foreach (
Component c in t.gameObject.GetComponents<Component>())
            {
                
MethodInfo m c.GetType().GetMethod(methodName);
                if (
!= null)
                    
m.Invoke(cparameters);
            }
            
t.parent;
        }
    }

    public static 
void BroadcastMethod(this GameObject gameObjectstring methodName)
    {
        foreach (
Component c in gameObject.GetComponentsInChildren<Component>())
        {
            
MethodInfo m c.GetType().GetMethod(methodName);
            if (
!= null)
                
m.Invoke(cnull);
        }
    }

    public static 
void BroadcastMethod(this GameObject gameObjectstring methodNameparams object[] parameters)
    {
        foreach (
Component c in gameObject.GetComponentsInChildren<Component>())
        {
            
MethodInfo m c.GetType().GetMethod(methodName);
            if (
!= null)
                
m.Invoke(cparameters);
        }
    }


(Offline)
 
Ответить с цитированием
Старый 18.08.2016, 03:14   #8
Жека
Дэвелопер
 
Регистрация: 04.09.2005
Адрес: Красноярск
Сообщений: 1,374
Написано 489 полезных сообщений
(для 882 пользователей)
Ответ: MonkeyBehaviour

Просто при старте игры проверять код на наличие нужных функций и если есть, то делать из найденных функций делегат
а куда денется исходный метод, он же останется.
например, если завернуть Update в делегат.
или ты про другой случай? какой?

Также можно расширить MonoBehaviour или Component своими функциями и использовать их.
Как вариант - навешивать на корневой объект сцены некий менеджер скриптов, который пробежит по всем дочерним объектам и компонентам, соберёт все экземпляры твоего переопределённого Behaviour'а, и менеджер будет в юнитевских методах Start, Update и прочих пробегать по спискам и вызывать делегаты.
По части Update получается модель привычного gameloop'a, где все логики перебираются и выполняются.

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

Пример с классом Message - тормозное решение, конечно.
Да и вызов через строковое имя метода опасен - отрефакторишь имя функции и "привет".

Последний раз редактировалось Жека, 18.08.2016 в 04:53.
(Offline)
 
Ответить с цитированием
Старый 19.08.2016, 12:36   #9
Жека
Дэвелопер
 
Регистрация: 04.09.2005
Адрес: Красноярск
Сообщений: 1,374
Написано 489 полезных сообщений
(для 882 пользователей)
Ответ: MonkeyBehaviour

К знатокам юнити и прочих движков.

Как лучше считать глобальную позицию и масштаб объектов?

Например, хочу сделать свойства localPosition и position.
Local возвращает просто вектор, а не-local делает проход по всем родителям и вычисляет позицию с учётом родительских позиций, масштабов, поворотов.

Так обычно делают или нет?

UPD: второй вариант - пересчитывать трансформы дочерних элементов при изменении свойств в родителе.
Для манки проблематично юзать такой подход, т.к. здесь все структуры - это классы, и нельзя запретить
изменение переменных внутри структуры.
То есть:

Если в юнити написать
transform.position.x = 200;
мы получим ошибку при компиляции.
За счёт этого можно перехватывать назначение переменной transform.position (а не поля "x" в структуре Vector) - и делать необходимые пересчёты.
В манки так сделать нельзя из-за ограничений языка.

Надо глянуть в сторону продвинутого Monkey2.

Последний раз редактировалось Жека, 20.08.2016 в 06:46.
(Offline)
 
Ответить с цитированием
Старый 20.08.2016, 07:20   #10
mingw
Нуждающийся
 
Аватар для mingw
 
Регистрация: 01.01.2016
Сообщений: 89
Написано 45 полезных сообщений
(для 84 пользователей)
Ответ: MonkeyBehaviour

Эм... в любом компоненте, обязательно должны быть только 3-4 метода... каждый называет их по своему, но я их назову так : Load(), Free(), Loop(dt#) и Draw() - думаю смысл их понятен. Все остальное - детали движка, которые легко реализовываются при помощи вышеописанных методов. Хотите LateUpdate() ? - Создайте хук, прицепите к нему нужный объект в методе Load() и вызывайте этот хук после всех обновлений... и выглядеть будет красиво и с ООП не придется заниматься сексом.

насчет второго вопроса... ну как бэ название говорит само за себя... localPosition - позиция объекта в локальной системе координат своего родителя. соотв. position - позиция относительно мировой системы координат. И для того, что бы узнать глобальное свойство без перебора всех родителей не обойтись ибо каждый родитель влияет на результат.
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
Жека (20.08.2016)
Старый 20.08.2016, 11:36   #11
Кирпи4
Социал-сычевист
 
Аватар для Кирпи4
 
Регистрация: 24.06.2011
Сообщений: 605
Написано 338 полезных сообщений
(для 1,332 пользователей)
Ответ: MonkeyBehaviour

Сообщение от Жека Посмотреть сообщение
К знатокам юнити и прочих движков.

Как лучше считать глобальную позицию и масштаб объектов?

Например, хочу сделать свойства localPosition и position.
Local возвращает просто вектор, а не-local делает проход по всем родителям и вычисляет позицию с учётом родительских позиций, масштабов, поворотов.

Так обычно делают или нет?
Это очень интересный вопрос. Однако, скажу что у меня сделано так:
У каждого объекта хранится полная трансформация в виде матрицы, а переменные позиций и углов находятся в локальных координатах. Дети обновляются только если изменилась трансформация родителя.

https://github.com/clashbyte/spriteb...orld/Entity.cs
__________________


(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
Жека (20.08.2016)
Старый 20.08.2016, 13:58   #12
Жека
Дэвелопер
 
Регистрация: 04.09.2005
Адрес: Красноярск
Сообщений: 1,374
Написано 489 полезных сообщений
(для 882 пользователей)
Ответ: MonkeyBehaviour

Хотите LateUpdate() ? - Создайте хук, прицепите к нему нужный объект в методе Load() и вызывайте этот хук после всех обновлений.
Можешь пояснить подробнее?
Псевдокод или в рамках юнити сущностей.
(Offline)
 
Ответить с цитированием
Старый 20.08.2016, 14:12   #13
Жека
Дэвелопер
 
Регистрация: 04.09.2005
Адрес: Красноярск
Сообщений: 1,374
Написано 489 полезных сообщений
(для 882 пользователей)
Ответ: MonkeyBehaviour

Кирпи4, в твоём коде есть Vector3 и Vec3, зачем так.
(Offline)
 
Ответить с цитированием
Старый 20.08.2016, 14:15   #14
Кирпи4
Социал-сычевист
 
Аватар для Кирпи4
 
Регистрация: 24.06.2011
Сообщений: 605
Написано 338 полезных сообщений
(для 1,332 пользователей)
Ответ: MonkeyBehaviour

Сообщение от Жека Посмотреть сообщение
Кирпи4, в твоём коде есть Vector3 и Vec3, зачем так.
Vector3 - это тип OpenTK, а Vec3 - мой тип, юзается при пришивании библиотеки к редактору, чтобы от OpenTK зависел только этот проект
__________________


(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
Жека (20.08.2016)
Старый 20.08.2016, 19:05   #15
mingw
Нуждающийся
 
Аватар для mingw
 
Регистрация: 01.01.2016
Сообщений: 89
Написано 45 полезных сообщений
(для 84 пользователей)
Ответ: MonkeyBehaviour

Модуль BlitzMax BRL.Hook :

Private

Type THook
	Field succ:THook
	Field priority
	Field func:Object( id,data:Object,context:Object )
	Field context:Object
End Type

Global hooks:THook[256]

Public

Function AllocHookId()
	Global id=-1
	id:+1
	If id>255 Throw "Too many hook ids"
	Return id
End Function

Function AddHook( id,func:Object( id,data:Object,context:Object ),context:Object=Null,priority=0 )

	Local t:THook=New THook
	t.priority=priority
	t.func=func
	t.context=context
	
	Local pred:THook
	Local hook:THook=hooks[id]
	
	While hook
		If priority>hook.priority Exit
		pred=hook
		hook=hook.succ
	Wend
	
	If pred
		t.succ=pred.succ
		pred.succ=t
	Else
		t.succ=hooks[id]
		hooks[id]=t
	EndIf
	
End Function

Function RunHooks:Object( id,data:Object )

	Local hook:THook=hooks[id]
	While hook
		data=hook.Func( id,data,hook.context )
		hook=hook.succ
	Wend
	Return data

End Function
1) Создаем переменную LateUpdateHook:Int = AllocHookId()

2) Вставляем объект и функцию-обработчик с помощью AddHook(LateUpdateHook, func, object, priority)

3) Ну и обработка всего этого дела - RunHooks(LateUpdateHook)
(Offline)
 
Ответить с цитированием
Ответ


Опции темы

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.


Часовой пояс GMT +1, время: 13:29.


vBulletin® Version 3.6.5.
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.
Перевод: zCarot
Style crйe par Allan - vBulletin-Ressources.com