My entity system for my game uses templates to lay out how entities are created. Something like:
EntityTemplateManager.register("zombie",
new PhysicsComponent(10), // speed
new SpriteComponent("zombie"), // graphic
new HealthComponent(100) // health
);
I like this part of the system because it makes sure I don't miss any parameters or screw up their type.
Then I can create a new entity like this:
entity : Entity = EntityTemplateManager.create("zombie");
Pretty straight forward.
What I do now, to actually create the entity, is have the EntityTemplateManager create 1 entity to use as a default, then I call clone() on the entity and it goes through and clone()s all of its components and returns the result.
Actual code:
public function clone() : Entity
{
var components : Vector.<Component> = new Vector.<Component>();
var length : int = _components.length;
for (var i : int = 0; i < length; ++i)
components.push(_components[i].clone()); // copy my local components to the new entity
return new Entity(_name, _type, components);
}
The problem is, every time I create (design) a new component, I have to write (design) a clone() method inside of it AND keep track of all the parameters that were called on the constructor to create a brand new, default stated component.
So I end up with junk like this:
public class ComponentX
{
protected var _defaultName : String;
protected var _defaultStartingAnimation : String;
protected var _defaultBroadcastAnimationEnded : Boolean;
protected var _defaultOffsetX : Number;
protected var _defaultOffsetY : Number;
// other stuff hidden
public function ComponentX(name : String, startingAnimation : String, broadcastAnimationEnded : Boolean = false, offsetX : Number = 0, offsetY : Number = 0) : void
{
super();
_defaultName = name;
_defaultStartingAnimation = startingAnimation;
_defaultBroadcastAnimationEnded = broadcastAnimationEnded;
_defaultOffsetX = offsetX;
_defaultOffsetY = offsetY;
}
public function clone() : Component
{
return new ComponentX(_defaultName, _defaultStartingAnimation, _defaultBroadcastAnimationEnded, _defaultOffsetX, _defaultOffsetY);
}
// other methods
}
I really don't like this -- it's wasteful and error prone.
How could I best store the parameters of the EntityTemplateManager.register() function (including the components' constructors parameters) and use that new storage to create entities from instead?
I've tried some generic clone() methods like with a ByteArray or describeType(), but they don't work with protected / private variables.
Ideas?
Few months ago I wrote my own Entity-Component manager too. I've done something like this (in your case):
EntityTemplateManager.register("zombie", [
{type: PhysicsComponent, attr: {speed: 10}},
{type: SpriteComponent, attr: {graphic: "zombie"}},
{type: HealthComponent, attr: {health: 100}}
]);
Registering a new template works as below:
private static var definitions:Dictionary = new Dictionary();
public static function register(templateName:String, componentDefinitions:Array):void
{
definitions[templateName] = componentDefinitions;
}
The create function retrieves the specified component template and create components like this:
var components:Vector.<Component> = new Vector.<Component>();
// Create all components
for each (var definition:Object in componentDefinitions) {
var componentClass:Class = definition.type;
var component:Component = new componentClass();
// Set default parameters
for (var prop:String in definition.attr) {
component[prop] = definition.attr[prop];
}
components.push(component);
}
// Then create the entity and attach the freshly created components.
// ...
Related
I am creating a GUI for selecting a function in a Unity custom EditorWindow inspired by the UnityEvent GUI. I can't get UnityEvent itself to work; using a EditorGUILayout.PropertyField and referencing the UnityEvent member as a serialized property produces an empty foldout.
I have choosing a function working, but I can't figure out how to allow the user to specify the function parameter arguments.
using System.Reflection;
int functionIndex = 0;
MethodInfo[] methods = typeof(LvlGenFunctions).GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
string methodNames = new string[methods.Length];
for(int i = 0; i < methods.Length; ++i)
{
methodNames[i] = methods[i].Name;
}
functionIndex = EditorGUILayout.Popup(functionIndex, methodNames);
methods[functionIndex].Invoke(null, null);
I can get the ParameterInfo and thus the ParameterType, but I don't know how to create an input GUI field to specify an appropriate parameter argument.
How can I create a Unity IMGUI input field for a parameter whose type is determined by reflection?
To be clear
EditorWindow : ScriptableObject and ScriptableObject : Object
SerializedObject work with UnityEngine.Object so you can use this constructor:
new SerializedObject(Object target);
EditorWindow
public UnityEvent OnSomeEvent;
private SerializedObject serializedObject;
private SerializedProperty property;
private void OnEnable()
{
serializedObject = new SerializedObject(this);
property = serializedObject.FindProperty(nameof(OnSomeEvent));
}
private void OnGUI()
{
EditorGUILayout.PropertyField(property, true);
}
Now you have the inspector but it's not something that you want.
Problem is it`s too ugly.
Update your code like below:
public UnityEvent OnSomeEvent;
private SerializedObject serializedObject;
private SerializedProperty property;
// new fields
private UnityEventDrawer EventDrawer;
private GUIContent Content;
private void OnEnable()
{
serializedObject = new SerializedObject(this);
property = serializedObject.FindProperty(nameof(OnSomeEvent));
// new initialization
EventDrawer = new UnityEventDrawer();
Content = new GUIContent(property.displayName, property.tooltip);
}
private void OnGUI()
{
// new drawer
var eventHeigth = EventDrawer.GetPropertyHeight(property, label);
var rect = EditorGUILayout.GetControlRect(true, eventHeigth);
EventDrawer.OnGUI(rect, property, label);
}
I need to create some enum values, give it a default value and then compare it.
I have this enum class
public class Car
{
public static const Tesla:int = 1;
public static const Ford:int = 2;
}
How do I initiate a new Car enumn variable with a default value of "Tesla" and how do I compare the variable? I'm looking for something like this:
public var c:Car = new Car(Car.Tesla);
if (c == Car.Tesla){
// Do something
}
Edit, it is now changed to the following:
public final class Car
{
public static const Tesla:String = "tesla";
public static const Ford:String = "ford";
}
And in the mxml file:
public var c:String = Car.Tesla;
if (c == Car.Tesla){
// Do something
}
I have this enum class
Just so we're on the same page about it: that's not an enum and there are no enums in as3. The language doesn't have that feature.
How do I initiate a new Car enumn variable with a default value of "Tesla" and how do I compare the variable?
You cannot, because Car is a type and the static properties it has are of type int which is something completely different.
What you can do is this:
var c:int = Car.Tesla;
if (c == Car.Tesla){
// Do something
}
If you want to have a Car object instead, add a brand property to the class of type int, which you can then assign the value of your constants to:
var c:Car = new Car();
c.brand = Car.Tesla;
if (c.brand == Car.Tesla){
// Do something
}
You could also add a parameter to the constructor and insert the value there.
Btw. changing
public static const Tesla:int = 1;
to
public static const Tesla:String = "tesla";
will give you the chance to get more meaningful values during debugging. The built in constants like MouseEvent.CLICK are defined this way.
how to pass a values dynamically to an Marionette.CompositeView during run time? like in java we create a method like the following
package com.test.poc;
public class SampleMethod {
public int add(int a, int b) {
return a + b;
}
public static void main(String[] args) {
SampleMethod method = new SampleMethod();
int firstValue = 90, secondValue = 90;
System.out.println("add : " + method.add(firstValue, secondValue));
}
}
the above is the simple java code anybody can understand like the above how to create and pass arguments to Marionette.CompositeView and work on them?
Best Regards
at the moment you instanciate a view, you can pass whatever arguments you want. normally you pass the model and the collection to be rendered in the compositeView, but you can pass more data if you need.
var MyCompositeView = Backbone.Mationette.CompositeView.extend({
initialize : function (options){
this.dataValue = options.dataValue1;
this.helperObject = options.helperObject;
this.useValues();
},
useValues: function () {
console.log(this.dataValue);
}
});
var helperObject = {
value3 : "I have a value",
value4 : "I dont!"
}; /// a js object literal
var myModel = new MyModel();
var myCollection = new MyCollection();
var myCompositeView = new MyCompositeView({model:myModel,
collection:myCollection,
helperObject:helperObject,
dataValue1:"Hi there"});
notice that Im passing 4 values in the at the time to intanciate the view, and Im reading just two of them, the model and the collection will be handled by marionette, but the other two you can read them in your initialize function.
hope that helps.
class Foo {
public function bar():void { ... }
}
var clazz:Class = Foo;
// ...enter the function (no Foo literal here)
var fun:Function = clazz["bar"]; // PROBLEM: returns null
// later
fun.call(new Foo(), ...);
What is the correct way to do the above? The Java equivalent of what I want to do is:
Method m = Foo.class.getMethod("bar", ...);
m.invoke(new Foo(), ...);
Actual code (with workaround):
class SerClass {
public var className:String;
public var name:String;
private var ser:String = null;
private var unser:Function = null;
public function SerClass(clazz:Class):void {
var type:XML = describeType(clazz);
className = type.#name;
// determine name
name = type.factory.metadata.(#name=="CompactType").arg.(#key=="name").#value;
// find unserializer
var mdesc:XML = XML(type.method.metadata.(#name=="Unserialize")).parent();
if (mdesc is XML) {
unser = clazz[mdesc.#name];
}
// find serializer
var sdesc:XML = XML(type.factory.method.metadata.(#name=="Serialize")).parent();
if (sdesc is XML) {
ser = sdesc.#name;
}
}
public function serialize(obj:Object, ous:ByteArray):void {
if (ser == null) throw new Error(name + " is not serializable");
obj[ser](ous);
}
public function unserialize(ins:ByteArray):Object {
if (unser == null) throw new Error(name + " is not unserializable");
return unser.call(null, ins);
}
}
Here the function bar only exist when your class is instanciated :
var foo:Foo = new Foo()
var fun:Function = foo.bar // <-- here you can get the function from the new instance
if you want to access it directlty you have to make it static:
class Foo {
public static function bar():void{ ... }
}
now you can access your function from the class Foo:
var fun:Function = Foo.bar
or
var clazz:Class = Foo
var fun:Function = clazz["bar"]
I am not sure about what you are intending to do.
However AS3Commons, especially the reflect package have API's that let you work with methods, instances and properties of a class.
There are also API methods to create instances of certain class types on the fly and call their respective methods.
Cheers
It's not
fun.call(new Foo(), ...);
Use instead since no parameters are required for the function
fun.call(clazz);
The first parameter as specified by adobe docs.
An object that specifies the value of thisObject within the function body.
[EDIT]
Forgot to point out you have to instantiate a non-static class with the "new" keyword.
var clazz:Class = new Foo();
[EDIT2]
Ok I played around and think I got what you want.
base.as
package{
public class Base {
public function Base() {
trace('Base constructor')
}
public function someFunc( ){
trace('worked');
}
}
}
//called with
var b:Base = new Base( );// note I am not type casting to Class
var func:Function = b.someFunc;
func.call( );
My workaround is to store the function name instead of the Function object.
var fun:String = "bar";
// later...
new Foo()[fun](...);
Is it possible to get autocomplete-functionality on objects keys?
var obj:Object = new Object();
obj.name = "AName";
obj.weight = "100";
When I type obj. -> i would like to see the keys(name,weight);
Thanks
Flash builder autocompletes only those properties/methods that are defined in the class (Defined might be a wrong word here, but I guess it is clear what I meant.) It does not autocomplete properties added in this way. As far as I know, this is not possible with flash builder.
create a class :
package {
public class NameWeightVO {
public var name : String;
public var weight : Number;
public function NameWeightVO(pName : String = "", pWeight : Number = 0) {
}
}
}
then you can create a new NameWeightVO Object with autocomplete functionality:
var nameWeightVO : NameWeightVO = new NameWeightVO();
nameWeightVO.name = "name";
nameWeightVO.weight = 10;