Serialization/Deserialization is hidden Diablo inside Unity.
You can fight it( remember you are on their ground!!!) and create your own custom system based on Protobuf, JSON (I dislike it)... or some other, or you can bend the knee. I choose to make hybrid.
I left Unity to take care of everything that subclass UnityObject + UnityEvent and create custom serialization for the rest thru C# built in BinaryFormatter. BinaryFormatter know how to serialize .NET types, but for Unity primitive types( Vector3, Rect, AnimationCurve,...) you need to create Surrogates. Which is more boring then some complex task.
Part of custom serialized data should be saved somewhere, so can be deserialized. Some solutions are using binary files (ex .dat,.bin) which is ok. But I disapprove solutions that put binary data, previous convert into Base64, into PlayerPref as string. As I found out that Unity know how to serialize ByteArray, I implemented new ISerializationCallbackReceiver and return the ball back to Unity letting Unity ser/des ByteArray of custom serialized data.
Where to use it?
You can create Blackboard with list of variables that can be add/change/removed without code having visual programming in hand.
You can create your variable inside your inspectors/editors:
Then you can use Raw value ex. 5.6f or Bind to some other GameObject/Component float property or reference variable from Blackboard making your tools more flexible.
No Curves No Sex.
Seem that curves are one of most important things in Unity.
Why Unity not opensource the editor components, similar like Unity UI 5.0 model, or at least to remove those internal modifiers to component's classes is MYSTERY. Here it is Unity Curve Editor exposed so you can used it Editors or Inspectors.
Curve editor inside Inspector where you can add/edit/remove multiply curves. https://youtu.be/CAV_faETDvA?t=364
Great news that Unity reveal the code of new UI. But why they didn't reveal source of Editor/Inspector Components?. At least why they not remove those fucken internal and sealed from code, so we can use , AvatarPreview, AnimationTimeLine .... without doing lot of Reflection mambo jumbo.????
Use it like this:
EditorGUILayoutEx.CustomTimeLine (ref eventTimeValues,ref eventTimeValuesPrev, ref displayNames, ref eventTimeValuesSelected,-1,
onAdd,oDelete,onClose,onEdit,onDragEnd
);
Source:
EditorGUILayout.Popup EditorGUILayout.IntPopup,FloatPopup...
No selection event, no generic objects, no way to format button appearance, no way to set separator or disable items...
public class EditorGUILayoutEx
{
private static object _CustomPopup_SelectedObject;
private static int _CustomPopup_ControlID = -1;
private static int _CustomPopup_SelectedIndex = -1;
public delegate void MenuCallaback <T> (int selectedIndex,T SelectedObject,int controlID);
public delegate void EventCallback (int ownerControlID,Event e);
Use like this (returning Index or Generic Object with Arrays or Lists as values):
I beleave that Unity community is quite large but I've feeling that SHARED RESOURCE & SOLUTIONS about any topic are quite few. When you google
for something for Flash you can find almost everything if not everything AND Its FREE. When you google for something for Unity you mainly finish on forums/answers/references of Unity web site and at the end you finish to ASSET Store with the song Money,Money....Of course in Flash you have polished paid components but you have tons free. Game design or knowledge shouldn't be monopoly only to companies with fat purse..
I need simple compact asset loader.
What I found was terrible example:
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour {
public string url = "http://images.earthcam.com/ec_metros/ourcams/fridays.jpg";
IEnumerator Start() {
WWW www = new WWW(url);
yield return www;
renderer.material.mainTexture = www.texture;
}
}
and ofcourse their UnityPro paid contra part AssetBundle.LoadAsync..., some quzzy solution on answers forum and ofcourse paid Assets in AssetStore.
In Flash you have for example BulkLoader or many other which are high quality and again free.(COMMUNITY?). And ofcourse their paid loaders with prof support,documents,thought tested....
I could remember MSDN in past as bad documentation reference but today is much much better with more examples but that's not the case with Unity docs.
Overall against 2 line examples teaching beginners how to do unusable(would you define all you urls manually as public members and you will yield(wait) in sequence one by one....!?) and crap code.
While building it I tried to adapt some good practices of Flash Loaders thinking that if you can do it in AS3 with 10x stronger language as C# you can do anything.
Define what need to be loaded and listen for loading status.
Loader request = new Loader();
request.Add("http://images.earthcam.com/ec_metros/ourcams/fridays.jpg");
request.Add("some.xml");
request.LoadComplete += new EventHandler>>(onLoadComplete);
request.Error += new EventHandler>(onLoadError);
request.LoadItemComplete += new EventHandler>(onLoadItemComplete);
request.load();
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using System.Collections;
namespace ws.winx.unity
{
public class LoaderEvtArgs : EventArgs
{
public readonly T data;
public LoaderEvtArgs(T data)
{
this.data = data;
}
}
public class Loader:IDisposable
{
public event EventHandler>> LoadComplete;
public event EventHandler> Error;
public event EventHandler> LoadItemComplete;
protected bool _isRunning=false;
private static List _wwwList;
private static List _queueList;
protected static List queueList
{
get { if (_queueList == null) _queueList = new List(); return _queueList; }
}
protected static List wwwList{
get{ if(_wwwList==null) _wwwList=new List(); return _wwwList; }
}
private static MonoBehaviour _behaviour;
protected static MonoBehaviour behaviour
{
get { if (_behaviour == null) { _behaviour = (new GameObject("WWWRequest")).AddComponent(); } return _behaviour; }
}
public void load()
{
if (!_isRunning)
{
_isRunning = true;
behaviour.StartCoroutine(check());
if (wwwList.Count != queueList.Count)
{
foreach (WWW www in queueList)
{
queueList.Add(www);
}
}
}
}
public void Add(string url){
if (Application.isEditor)
url = "file:///" + url;
WWW w = new WWW(url);
Add(w);
}
public void Add(WWW www)
{
wwwList.Add(www);
queueList.Add(www);
}
IEnumerator check()
{
int i;
WWW www;
while (true)
{
i=0;
while (queueList.Count>i)
{
www=queueList.ElementAt(i);
if (www.isDone)
{
if (!String.IsNullOrEmpty(www.error))
{
if (Error != null)
{
//Error(this,new LoaderEvtArgs(www));
Dispatch(Error, new LoaderEvtArgs(www.error));
}
else
{
Debug.LogError(www.error);
}
}else
if (LoadItemComplete != null)
Dispatch(LoadItemComplete, new LoaderEvtArgs(www));
//LoadItemComplete(this, new LoaderEvtArgs(www));
queueList.RemoveAt(i);
}
i++;
}
if (queueList.Count == 0)
{
_isRunning = false;
if (LoadComplete != null)
//Dispatch(LoadComplete, new LoaderEvtArgs(wwwList));
LoadComplete(this, new LoaderEvtArgs>(wwwList));
yield break;
}
yield return new WaitForSeconds(0.5f);
}
}
private void EndAsyncEvent(IAsyncResult iar)
{
var ar = (System.Runtime.Remoting.Messaging.AsyncResult)iar;
var invokedMethod = (EventHandler>)ar.AsyncDelegate;
try
{
invokedMethod.EndInvoke(iar);
}
catch
{
// Handle any exceptions that were thrown by the invoked method
Console.WriteLine("An event listener went kaboom!");
}
}
protected void Dispatch(Delegate del,LoaderEvtArgs args)
{
Delegate[] delegates = del.GetInvocationList();
foreach (Delegate d in delegates)
((EventHandler>)d).BeginInvoke(this, args, EndAsyncEvent, null);
}
public void Dispose()
{
if(_queueList!=null) _queueList.Clear();
if(_wwwList!=null) _wwwList.Clear();
}
}
}
If you use BeginInvoke so you not block main thread while handling instead Invoke, callback would be executed on the another thread so you need to use some sync mechanism with Unity main thread as WWW can't be used outside. You could use QUEUE of Action in main thread, fill in Invoking thread and execute in main thread again.
I really wanted to prove whole concept so I left this post underdeveloped. I'm proud to annouced that concept is proved not just web but Win,OSX and Droid.
Main idea to use browser GamePad API (Still not supported by Safary and not W3C standard) came from that I couldn't find way how to solve the problem recognizing wheel turning direction as Unity static float GetAxisRaw(string axisName) return from full left to full right turn -1 to 1 to -1. So I couldn't find what is left and write. I'm dumb and hate Unity Ultimate InputManager tree control so I decided to call GamePad API thru Mono behaviour and SendMessage
I succeed on both Chrome and FF despite their GamePad API aren't the same.
public void HaveGamepadEvents()
{
Application.ExternalEval(
"if('GamepadEvent' in window) UnityObject2.instances[0].getUnity().SendMessage('WebHIDBehaviourGO','onHaveGamepadEvents','1');" +
"else UnityObject2.instances[0].getUnity().SendMessage('WebHIDBehaviourGO','onHaveGamepadEvents','0');"
);
}
First have events and second need pool loop in order to track connecting/disconnecting of devices
if (hasGamepadEvents)
{
_hasEvents=true;
Application.ExternalEval(
// "window.addEventListener('gamepadconnected',function(e){ var buttons = []; for (var i = 0; i < e.gamepad.buttons.length; i++) buttons[i] = e.gamepad.buttons[i].value; UnityObject2.instances[0].getUnity().SendMessage('WebHIDBehaviourGO','onDeviceConnectedEvent',JSON.stringify({ id: e.gamepad.id, axes: e.gamepad.axes, buttons: buttons, index: e.gamepad.index }))});" +
// "window.addEventListener('gamepaddisconnected',function(e){ UnityObject2.instances[0].getUnity().SendMessage('WebHIDBehaviourGO','onDeviceDisconnectedEvent',e.gamepad.index.toString())});"
"window.addEventListener('gamepadconnected',function(e){ UnityObject2.instances[0].getUnity().SendMessage('WebHIDBehaviourGO','onDeviceConnectedEvent',JSON.stringify({ id: e.gamepad.id, numButtons:e.gamepad.buttons.length, numAxes:e.gamepad.axes.length, index: e.gamepad.index }))});" +
"window.addEventListener('gamepaddisconnected',function(e){ UnityObject2.instances[0].getUnity().SendMessage('WebHIDBehaviourGO','onDeviceDisconnectedEvent',e.gamepad.id)});"
);
}
else//manuall check
{
Timer enumeratorTimer = new Timer(1000);
enumeratorTimer.Elapsed += new ElapsedEventHandler(EnumerateGamepadsTimedEventHandler);
enumeratorTimer.Enabled = true;
}
Unity Input Manager pain is lasting for years. On Unity site you can find request on the forum for InputManager programmatic access dating from 2004, and some new from 2014. Futile.
Its good they are planning to :). Now see the date 2009 of feedback, then see your computer date. Calculate difference in years. Ultimate Game Development with state of the art InputManager system :D.
Problems:
Input handling is beast overall taken. There difference in how diffrent OSes handle input. Even same controller is attached meaning identical hardware and communication protocol different OSes would handle differently cos of at least byte order. Then difference come from different drivers used in different OS so for example OSX recognize XBOX360 Wireless controller POV as 4 buttons and WIN as 2 Axes. In same OS you can have multiply default handling ways like Winmm and DInput,XInput in Windows. Even in same OS but different version and same handling way could have differences(ex. Winmm doesn't recognize sliders/triggers in WIN7 and have no problem in WIN8). On top all that come problems with Unity mono threading when add/remove device notification and input are async interrupt based.
1) Editor and Ingame input controller mapping. Unity Input Class works only in Play mode so you are left to blindly mapping inside Input Manager tree view. Changing mapping later need game restart. 2) Handling input in code based on states/tag abstracting real input.
Some sort of abstraction is done thru InputManager well known "Horizontal" and "Vertical", but that abstraction is still bonded to axes and buttons and not to actions/states of the game(one of those might be AnimateStates of Mecanima).
3) Saving and restoring user preferences
PlayerPref might do the trick if filesize is not bigger then 1 megabyte. You can use .asset or also load InputManager.asset during runtime and save but might involve extra efforts when WebPlayer or Droid is target. File type that can be readable by humans and machines, and easy exchangeable( players can exchange their game settings) between them might be XML.
4) Recognize positive and negative part of the axis.
Unity recognize Axis as whole part and gives value 1 <--- (-1) ---> 1. It is expected to gives value from 1<---- (0) ----> -1, so turning left/right on wheel input controller or joystick push/pull forward/backward can be achieved, trigger shooting rapid fire... .
5) OS independent driver and easy expansion with drivers supporting other devices and special properties
Unity internal input handler might not recognize the controller or it will identify layout in different system differently and would not offer support of device extra features like force feedback, IR pointer, accelerators, gyros ...of modern input controllers. So instead of use pluggin-paid OS dependent drivers, seem much better OS dependent HID interfaces with OS independent pluggable drivers.
6) Handling input axis and buttons as digital or analog
In Unity thru Input class you can handle axes only as analog and buttons as analog/digital. Its handy to have axes as digital events UP, DOWN , HOLD...
7) Create combination of inputs that would trigger action/state
Unity doesn't offer out of the box combining input actions like 2 keys in row, button press and axis move, ....like in fighting game scenario 2 times joystick left + fire (Mustafa kick Cadillacs and Dinosaurs)
8) Handling inputs by events
Unity is planned as single threaded getting out of the way of multi-threading headache. Complex input handling require input events as Update function got overcrowded. This involve multi-threading and need extra attention towards synchronization with Unity main thread.
9) Plug and play instead of plug and pray.
Attach or remove controllers while game is running and continue playing. Somekind of add/remove notification can be done with tracking of number of joystickNames thru Input.GetJoystickNames() but when 2 or more joysticks are of same type that way having same names distinction isn't possible. Also order of names in list is might change after reattaching same device for ex. previous Joy1 become Joy4 (hack *). I noticed that some controllers(XBOX wireless under OSX) are hard bind to Joystick# no matter position in GetJoystickNames list.
10) Profiles - Layouts
Show user friendly appearance of mapped input like Left Stick X, Bumper, Forward, Triangle, Square,....instead of Joystick1Button15, Joystick3Axis12... Switch profiles by assigning different controller to different player. *Entries for every Joystick#+Buttons# (Joystick1Button1,...Joystick2Button2... inside InputManager multiplied by factor for variation with keys and settings.
Input Mapper system try to address above issues.
1) Ingame input controller mapping.
Input Mapper allows you easily to map game controller input to custom states.
2) Handling input in code based on states/tag abstracting real input.
InputMapper API looks very similar to Unity API's, but they offer abstraction on 2 levels. First you not program with real inputs like KeyCode.Q or Joystick1Button99, but with states which also allows player to map different input to same action state.
//atOnce (default=false)
//Function returns true when combination pressed in sequence If set to true function return true
//when all keys/buttons in combination are pressed.Modifier like behaviour (LeftCtrl(-)+B)
bool bCombo = (InputManager.GetInput((int)States.Jump,InputPlayer.Player.Player0,atOnce
));
You are handling input of State.Jump, no matter what Player have mapped to that input, no matter is KeyCode.Q or Joystick1Button99 or something else.
3) Saving and restoring user preferences
Saving would export your settings to .xml or .bin file and States.cs will be generated containing enum of your mapped states.
public enum States:int{
Wave=1397315813,
MyCustomState=-1624475888,
Now you can forget about:
// static int idleState = Animator.StringToHash("Base Layer.Idle");
// static int locoState = Animator.StringToHash("Base Layer.Locomotion");
as we are thought in Unity tutorials, but you can use States.[some state].
4) Recognize positive and negative part of the axis.
System recognize Forward,Backward,Left,Right of POV axis and Positive/Negative of normal axes.
This is useful when you are using your joy stick as button.
Intent is states not to be addressed as string as in above example given just for clarification. Use Enums!
5) OS independent driver and easy expansion with drivers supporting other devices and special properties
I understood Unity couldn't support all game devices but some system that allows simple binding of specific driver might be good idea. Why? All AAA Games provide list of top controllers used and tested with their game and they would work out of the box. The owner of rest controllers might relay on manufacturer or OS.
(Yeah they have pluggins.....).
So instead building pluggins for all OSes, you use InputMapper system built-in mini HID interface systems for (Win,Web,Droid and OSX) and OS independent driver. (Yes you can make C driver cross compile to anything...but....discussion need lot of effort).
If specific driver is found for your device it would be handled by it:
//supporting devices with custom drivers
InputManager.AddDriver(new XInputDriver());
If there is no specific driver for device default driver (WinMMDriver for Win and OSXDriver for OSX) would try to handle device.
You can use UnityDriver as default driver having default Unity handling functionality, and still handle specific devices with specific drivers.
Swap your InputManger.asset with InputManager.asset from github source code.
Warring:Unity
doesn't make distinction between triggers/axis/poitOfView and doesn't
make controller distinction as multiply instances of same type have
same name and can hard code index of devices no matter position in
GetJoystickNames list.
Specific driver implementation guidelines:
Your implementation of custom device driver had 2 entry points that need implementation:
ResolveDevice where HIDInterface provide device info from the OS, you can check VID and PID to decide to handle and init device properties and structures, or not returning null, and Update function to query device by use of provided Read/Write methods methods and fill the JoystickDevice structures, so can be accessed by InputManager. Scare about handling few bytes??? :) Originally OS implementation of ResolveDevice point is by using Report HID Page from device description, but that restrict using devices that aren't planned to work with that OS, like Wiimote,Xbox Gamepad... In Input Mapper you need simply to extend JoystickDevice Class, which already contains some basic functionality supporting axes and buttons, by adding specialized properties of device you are planning to support. Update point is synchronization point with Unity. System use high level synch and async ReadXXX methods and return buffered byte array. What each of bytes means is easy to decipher as there are many web sites about any devices. Developer task is to fill JoystickDevice structure with values processed from byte array returned. Write methods are used for setup the device...like light leds, start FFD, enable disable....
Devices used during testing: XBox360W controller, ThrustMaster Wheel FFD, Wiimote + Nunchuk.
One classic Gamepad controller, one Wheel and complex controller.
6) Handling input axis and buttons as digital or analog
Second abstraction is that you can use digital or analog output no matter your real input source is, meaning for ex. Joystick Axis is analog source giving input from 0.0 to 1.0, but can be tracked as digital true/false, or mouse button which is digital would return float values 0.0 to 1.0 while hold .
7) Create combination of inputs that would trigger action/state
So you not need to open that nasty InputManager Tree component. Connect your test controller to your computer. Open InputMapper Editor in Window->Editor .
Map inputs to state by just clicking keys/mouse/buttons or moving joystick as SINGLE, DOUBLE and LONG and make combination of inputs(Combos). Primary and secondary. In bellow example I've map to Wave state combo of Mouse0 double clicks + Joystick1AxisYNegative (Long pull backward of the Joystick) + double click of letter Y.
You can set modifier format or clicks sensitivity.
Select Player and then profile to start mapping states to inputs. When controller is attached, its profile would appear in profile list under "default". You can use "Clone default" to clone states from default to controller specific settings. Then you can change mappings. You can also clone Player to other players and add difference changes.
8) Handling inputs by events
As Update method get overcrowded, library offer modern input handling solution by use of Event based system.
InputManager.addEventListener((int)States.Wave).UP += onUp;
InputManager.addEventListener((int)States.Wave).DOWN += onDown;
void onUp(object o, EventArgs args)
{
Debug.Log("Up");
}
void onDown(object o, EventArgs args)
{
Debug.Log("Down");
}
or use editor:
Hardcode developers can manually map input to states and even mix with loaded settings. This is useful mainly for testing.
05.10.2014 XInput driver pure C# (No DirectX xinput.dll wrappers)
13.10.2014 (OSXDriver default driver pure C#)
17.10.2014 (Thrustmaster Wheel FFD and XBOX360W working on OSX)
CONCEPT PROVED!!!
18.10.14 Added much simplified sample scenes then previous hard core sample
Plans: 1)Make some kind of pluggin structure so you put ur .dll driver in same folder and get reflected in runtime
2) Adding accept time between 2 inputs
24.12.2014 HappyHolidays. In past 2 weeks I was working day/night to cure child diseases of InputMapper. Tons of bugs were smacked, many sections are heavily tested, optimized, synced or redone and new features like mapping axis as whole or as from any device easily were introduced.
10.01.2015 v1.2 Announced. Profiles, multiplayer and big redesign of the system.
Issues:
InputMapper might crash Unity Win????
Generation of "States.cs" doesn't work in OSX???
and couldn't find logic why Unity masters teach using this way.
I couldn't remember all those names and really I wanted to be free redesigning nodes in Animator without to define/remove those states again and again in code. Also I wanted Intellisense in editor, readable states and still have speed of working with integer hashes not strings.
You can create even much complex generation and compilation with use of T4 Templates under the .NET, but Mono.TextTemplating is still unfinished so I needed to update/upgrade in order to reach my goal. Next create .tpl (some extension don't use original .tt cos MonoDevelop will try to compile right away and isn't stable at all) text file with content:
<#@ template language="C#" debug="true" hostspecific="true" #>
//THIS IS AUTO-GENERATED FILE. DON'T CHANGE
namespace <#= Namespace #>{
public enum <#= EnumName #>:int{ <#= Values #>
}
}
In Editor Script you can loop thru AnimatorControler's States and build StringBuilder of comma separated StateName=HashValue,...pairs.
In template #Variables will be replaced with content of the generator Session variable.
You need to refresh AssetDatabase cos even file is generate wouldn't be seen by Unity until refreshed. You can also track AssetDatabase for changes and then generate when Scene/Animator is saved. I've such solution so its possible but changed to generate when click Save inside Editor Window.
In you code you can use it with Intellisense when you write States. <autocomplete> , will be also readable and undercover compiler will access state by integer hash.