Friday, August 31, 2018

 

Unity3D & spatially based audio reverb

Spatially based audio reverb is an audiosource echoing in a way that helps the listener determine the spatial location of the audiosource. This is the most helpful in VR applications where a listener can see the shape of the room. Seeing the shape of a room helps the listener predict how a sound will echo in it.

I've been working with applying spatially based reverb to a project. Initially I set out to use a *.unitypackage provided by Oculus, ovr_audio_spatializer_unity_1.27.0 https://developer.oculus.com/downloads/package/oculus-spatializer-unity/ . I ran into some difficulties getting a basic reverb to work. A colleague mentioned Unity comes with a primitive version of the OculusSpatializerReflection effect from this package. I set out to give the in-built version a try and had success.

The in-built version of OculusSpatializerReflection doesn't have as many features as Oculus's *.unitypackage. The major feature missing is the *.unitypackage allows you to define the location of the shoebox creating the reverb. The AudioListener can move freely inside of the shoebox. The primitive version built into Unity does not support that type of AudioListener movement. Instead it always has the AudioListener at the center of the shoebox creating the reverb. However, the primitive version is quite a bit easier to setup.

OculusSpatializerReflection setup

To set up OculusSpatializerReflection from a completely empty Unity project:
  1. Edit -> Project Settings -> Audio -> spatializer Plugin should be set to OculusSpatializer
  2. In the project panel, Right click -> Create -> Audio Mixer
  3. Add an OculusSpatializerReflection effect to the Master channel of the AudioMixer that you just created. Then setup that effect to be a very reflective hallway using the following properties:
    GScale: 1 - this value is essentially a multiplier from the Room XYZ
    E.Rflt On: 1 - E.Rflt stands for Early Reflections. They help bring out the spatial aspect of the reverb
    E.Rflt Rev On: 1
    Room X: 16 - Here you want the room to be like a big church. The algorithm doesn't handle long hallways well.
    Room Y: 16
    Room Z: 16
    Left: 0.97
    Right: 0.97
    Up: 0.97
    Down: 0.97
    Behind:0.97
    Front: 0.97
    Shared Rev Min: 1
    Shared Rev Max: 10000 - This seems to be the value corresponding to when echos get cutoff. Maximize this to ensure you get as few echo cutoff as possible.
  4. Create a new scene, the camera should be left at it's default position of 0, 1, -10
  5. Add an empty game object at 0,0,0 that has an AudioSource component with:
    AudioClip: set to any audioclip you want to test with, vocal1.ogg from the OculusNativeSpatializer.unitypackage worked well for this
    Output: Master (This is the channel of the audio mixer you created in step #2)
    Spatial Blend: 1
    Spatialize: checked
This method consistently applies a spatially based reverb. I tried to make it the most apparent blatant echo I could by making a long hallway, 1x1x200. If you have any ideas to make the echo stronger please do comment.

If reverb completely stops working you can sometime get it to work again by setting "E.Rflt On" to 0. pressing play. Stopping it. Then setting "E.Rflt On" back to 1.

There's more details at this forum post: https://forums.oculusvr.com/developer/discussion/69082/basic-reverb-in-oculus-spatializer-unity

Sunday, January 21, 2018

 

Unity3d Video Editing

Goal

How to use Unity3D 2017.x+ to make *.mp4s with overlaid text.

Update: The next day I stumbled on an Asset Store tool that does the exporting to an *.mp4 for you.

Why I didn’t go with Blender

I wanted to make a Facebook style video to run an ad. Which means take an existing video and overlay some text so that the video had some meaning when it was played without audio. Initially I tried to use Blender as I thought it’d be a great way for me to learn that tool. Plus, there are tons of tutorials on how to use Blender for video editing. Halfway through my first attempt I decided to find a way other than Blender due to:

  1. Blenders text overlay feature is difficult to work with. Overlaid strings of text can not be multi-line.
  2. I couldn’t figure out how to copy the animated graph window parts text objects, meaning my 4 pieces of text which each had 4 lines of text would require redoing my animated graph fade work 16 times.
  3. I once found a way Blender scrubbing a video wouldn’t be choppy on a low end computer, I was unable to re-find this method. Upping Blenders RAM usage wasn’t enough.

Why I went with Unity3D

Unity3D I was already familiar with. Yet I couldn’t find a single guide on how to use it to create *.mp4s. I realized I could use a screen recorder, like ObsStudio, to encode the output to an *.mp4. The more I thought about it the better Unity3D seemed to fit:

  1. Unity3D has pretty good UI tools.
  2. Unity3D 5.6 introduced a new VideoPlayer component.
  3. Unity3D 2017.1 introduced a new TimeLine tool.

Setting up Unity3D

Source Video

All we’re going to do is overlay text ontop of an already composed video. The first step is to make sure our video plays correctly in Unity3D. These steps were taken from a section of this youtube video: https://youtu.be/V8rwCWiRLWI?t=2m53s

  1. Start a new Unity3D project / scene
  2. Drop an *.mp4 source video file in the Unity3d project window. This will be the background video our text we be overlaid upon.
  3. Add a VideoPlayer component to an empty game object.
  4. Drag *.mp4 clip onto video player component.
  5. On the VideoPlayer component change Render Mode to “Camera Far Plane”.
  6. Drag the Main Camera into the Camera slot in VideoPlayer.
  7. Add an AudioSource component to the same VideoPlayer game object.
  8. Drag that AudioSource component into the Audio Source field of the VideoPlayer component.

Creating Text Objects

I wanted text to fade in over half a second, display for 5.5 seconds, then fade out over half a second.

  1. Add GameObject > UI > Text.
  2. To set the text to use the full view, open the Rect Transform’s red cross-hair positioning icon. Hold shift+alt and click the popup windows bottom right icon.
  3. In the Text component set the font size and Paragraph Alignment to what you prefer. Set the color to white.
  4. Add an Outline component to help the text stand out from your video. Set the alpha of the outlines’ black to 255.
  5. Add a Canvas Group component to this text.
  6. Make sure the Text object is selected in your hierarchy. Then open the Animation window and click “Create”.
  7. Click the red circle record button in the Animation window.
  8. Make sure the play head is at 0 and change the Texts’ Canvas Group components Alpha value to 0.
  9. Move the play head to 0:30 (half a second) and put the Alpha value back to 1.
  10. Scroll out until you see 6:30 on the animation track.
  11. In the Canvas Group.Alpha row right click under 6:00 and “Add Key”.
  12. Move the play head to 6:30 and set the Alpha back to 0.
  13. Turn off record mode and press the play preview button to make sure the fade works correctly.
  14. Find the saved *.anim file in the Project explorer. Click it to uncheck “Loop Time” in the inspector. This will make sure the same text isn’t shown again after it’s played once.
  15. Copy the text component as many times as you need with Ctrl+D. Change each one’s text to match what you want it to say.
  16. Disable all text gameobjects, they’ll be enabled by the timeline below.

Setting when text gets shown

The timeline feature of Unity 2017 is great for timing which objects are shown when. With how our text objects are setup all we need to do is enable them at the correct time.

  1. In Unity3D go to Window > Timeline
  2. Create a new empty gameobject called “Timeline”
  3. Click “Create” in the Timeline window
  4. Save it as TextTiming.playable
  5. Delete its only row, which should have a blue cap on the left (animation track) and contain a reference to the Timeline game object
  6. Add an Activation track to the timeline.
  7. Click the settings cog in the upper right of the Timeline window and change the view to Seconds instead of Frames.
  8. Resize the Activation track’s Active to be 8 seconds long
  9. Select the Activation track and press Ctrl+D for each copy of the Text object you created.
  10. Drag one Text object to each Activation track
  11. Move their Active components to start when the previous ends. It should look like a staircase.
  12. Extend the last Texts Active track to be as long as your video. Otherwise all text gameobjects activate.

Now when you press the Unity Editors play button you should see your video play with text being overlaid. You’re ready to record an *.mp4!

Setting up ObsStudio

To setup the video resolution in ObsStudio go to Settings > Video > Base Resolution. Here I also set “Common FPS Values” to “30”.

Next decide how the file will be saved in Settings > Output by setting its’ “Recording Path” and “Recording Format”

Recording from the “game” view in the Unity3D editor worked great for me. I made sure the Unity3D game window was also locked to the correct resolution by changing the resolution from “Free Aspect” to match the value I put in ObsStudios’ “Base Resolution”. More details on the game view control bar at https://docs.unity3d.com/Manual/GameView.html

Under Sources add a Display Capture. This shouldn’t show your entire monitor’s contents as long as you set the Base Resolution smaller than your monitors resolution. You do not want to scale the video with the red box handles, instead you want to pan it to the right part of the monitor by left clicking any part of the display view and dragging.

Recording

  1. Made sure the Unity3D game window was bigger than the set video resolution.
  2. Set the edge of the game view against the right edge of my monitor.
  3. In ObsStudio made sure the game window was centered in the display
  4. Pressed the “Start Recording” button in ObsStudio.
  5. Pressed play in Unity3D
  6. Waited until it played through what I wanted
  7. Pressed “Stop Recording” in ObsStudio

Video trimming

I used Windows 10 Photos to trim the video. Open the *.mp4 with Windows 10 Photos then use the edit tools in the upper right to easily define the trim. More details at http://www.tech-recipes.com/rx/56780/trim-videos-windows-10-photos/

Completed video editing unity scene.
Written with StackEdit.


Tuesday, April 04, 2017

 

uNet High Level API clients sharing objects

The goal of Unity3D's uNet HL(HighLevel)API is to sync an experience across multiple clients. This differs from uNets' low level networking in that the HLAPI delegates network tasks through a spawned prefab that inherits from NetworkBehaviour. Syncing is done through this prefab created for each player as soon as they join a server. Keeping a shared variable or gameobject sync'd between clients, using the HLAPI, also needs to be accomplished through the prefab spawned in by the HLAPI. The clients prefab sends a message to the server that it wants to update a shared resource. The server then makes an RPC call for all clients to update their copy of the shared resource. To illustrate the flow, lets update a shared variable (instead of a gameobject).

When the following NetworkBehaviour is used as the prefab being spawned by the HLAPI, a SharedIntExample variable will be synced across the server and all clients. Whenever a player updates their copy a message is sent to all other clients to get their copies of the variable to match, via the server.
public class NetworkSession : NetworkBehaviour
{
    public static int SharedIntExample;

    void Update()
    {
        // only do something if the local player is doing it. CmdUpdateShare() calling RpcShareUpdate() will make sure everyone else sees it
        if (isLocalPlayer && Input.GetMouseButtonDown(0))
        {
            CmdUpdateShare(SharedIntExample + 1);  // define how the value should be updated, in this case increment by 1
        }
    }

    [Command]
    void CmdUpdateShare(int newValue)
    {
        RpcShareUpdate(newValue);  // use a Client RPC function to update the value on all clients
    }

    [ClientRpc]
    void RpcShareUpdate(int newValue)
    {
        SharedIntExample = newValue;
    }
}
This works great. You can check it by binding a UnityEngine.UI.Text field to the NetworkSession.SharedIntExample (the only reason this int is static is for ease of testing its value). It's great, unless a client joins after the variable has already been updated. Luckily, requesting a refresh to all clients is easy. Add these two functions to the NetworkSession class above.
    private void Start()
    {
        CmdNewClient();
    }

    [Command]
    void CmdNewClient()
    {
        RpcShareUpdate(SharedIntExample);
    }
Not all clients needed a refresh. Only the newly joining client needed that update. Specifying an update to a single client isn't too difficult if you make use of the newer [TargetRpc] attribute. https://docs.unity3d.com/ScriptReference/Networking.TargetRpcAttribute.html Here we update CmdNewClient() to use a new [TargetRpc] decorated function.
    [Command]
    void CmdNewClient()
    {
        TargetShareUpdate(this.connectionToClient, SharedIntExample);
    }

    [TargetRpc]
    void TargetShareUpdate(NetworkConnection target, int newValue)
    {
        SharedIntExample = newValue;
    }
Now each client has the latest value of SharedIntExample, even if it joins after the value has been updated.

Before I figured out how to do this correctly, as above, and by using AssignClientAuthority (from stumbling on http://stackoverflow.com/questions/33469930/how-do-i-sync-non-player-gameobject-properties-in-unet-unity5/35504268). I tried to do it an unnecessarily complex way, which is described below, in strike-through, under "Wrong way of doing it".

Wrong way of doing it


Decide what needs to modify the integer; just the server, just the client, or both the server and the client. If the number is modified on the client: - [Command] should be used to modify the master instance of that number stored on the server.
public class NetworkSession : NetworkBehaviour
{
    [Command]
    public void CmdSetSlide(int toIndex)
    {
        // update servers version of SlideServer.Instance.SlideIndex
        MapStatus.Instance.SlideIndex = toIndex;
    }
}
If the number is modified on the server: - [SyncVar] should be used to update the copy of that number on each client to match what is stored on the server.
public class FindIdentity : MonoBehaviour
{
    void Start()
    {
        networkConn = GameObject.FindObjectsOfType()
            .Where(s => s.isLocalPlayer)
            .FirstOrDefault();
    }

    void Update()
    {
        if (networkConn != null // check if this is a client. networkConn should only be null when this code is running on the server.
            && networkConn.CurrentCity != MapStatus.Instance.CurrentCity)
        {
            onCitySyncVarChanged();
        }
        else
        {
            keyInCity();

            // catch city changes Server side
            if (MapStatus.Instance.CurrentCity != oldCity)
            {
                oldCity = MapStatus.Instance.CurrentCity;

                // update map on the server
                mapOfPois.CurrentMap = MapStatus.Instance.CurrentCity;

                // update city syncVar of all sessions
                foreach (var netSession in GameObject.FindObjectsOfType())
                {
                    Debug.Log("netSession.name)" + netSession.name);
                    netSession.CurrentCity = MapStatus.Instance.CurrentCity;
                }
            }
        }
    }
}
- clients that join late need to have their copy updated to match the server
    ///  Make sure a newly connected client gets the city selection made before they joined
    /// This override only runs on the client when the client has authority, which is after OnStartClient 
    public override void OnStartLocalPlayer()
    {
        base.OnStartLocalPlayer();

        Debug.Log("OnStartLocalPlayer " + this.name + " with " + CurrentCity + ". Asking server to update CurrentCity.");
        CmdReconnect();
    }

    [Command]
    public void CmdReconnect()
    {
        Debug.Log("CmdReconnect " + this.name + " with " + CurrentCity + " updated to " + MapStatus.Instance.CurrentCity);

        // uses the servers copy of CurrentCity to update the SyncVar
        CurrentCity = MapStatus.Instance.CurrentCity;
    }
Since the prefab is spawned when a client joins you can't have it easily reference objects already in the scene by dragging gameobjects into fields in the inspector. You can create another gameobject to do this interfacing. It will need to search for the NetworkBehaviour, this is done above in the FindIdentity's Start(). That was the wrong way of doing it. The cleaner alternative of is above the "Wrong way of doing it" title.

Friday, September 18, 2015

 

Unity3D Video/Movie using Sprite frames

On a Unity3D 5.x project we needed alpha blended Movies to play full screen on our app. We tried the asset store plugin MobileMovieTexture. For MobileMovieTexture to have decent performance the movies need to be fairly small, 320x240. 320x240 looks pixelated, especially on tablets. Using sprite sheet animation worked out much better for us, we were able to get as much performance as the 320x240 video but with 640x480 resolution at the cost of build size.

Here's an implementation of the Unity project described below.

Here's how to make a sprite behave more like a video:
1. Create a single image for every frame of the movie you'd like to use. I ended up doing 15 frames for every second of movie. One way to extract frames from a movie file is with an ffmpeg command: ffmpeg -i "infile.mp4" -f image2 -vf fps=fps=15 img%03d.jpg
2. (Optional) Make any modifications to the frames. For example, convert them to *.png's with transparency.
3. Import all the image file frames into your unity project.
4. In Unity's project window, shift-select all of the frames at once to bring up their properties in the inspector.
4.1. Change the Texture Type to "Sprite (2D and UI)"
4.2. Change the Packing Tag to the name of the movie. This name does some behind the scenes optimization by putting all these frames are on a single sprite sheet or image atlas without any other sprites.
4.3. Make sure "Generate Mip Maps" is unchecked. Mip Maps take up space that we won't be using as the sprites will stay the same distance from the camera.
4.4. Press "Apply".

5. From the project window drag the selection of all the frames together into the scene view. This causes Unity's 2D engine to auto-generate an animator and sprite animation using all selected frames.

6. Change the animation to match the FPS you specified in step #1.

Hit the Editors play button you should see your video play.

Full Screen Video

7. Add a camera as a child to the sprite and give it the following settings
7.1. Offset it's position from the sprite by setting z-position to -1
7.2. Clear Flags: Depth - this allows other content to be rendered underneath what this camera see's
7.3. Projection: orthographic - using orthographic makes calculating the screen size easier
7.4. Depth: 1 - tells unity to render this camera above the main camera

8. Create ScaleToCamera.cs script
using UnityEngine;

public class ScaleToCamera : MonoBehaviour
{
    public new Camera camera;
    private SpriteRenderer spriteRenderer;

    void Start()
    {
        if (camera == null) camera = Camera.main;

        spriteRenderer = this.GetComponent<SpriteRenderer>();
        if (spriteRenderer != null)
        {
            scaleSprite();
        }
    }

    private void scaleSprite()
    {
        if (camera.orthographic == false)
        {
            Debug.Log(this.name + " can't run ScaleToCamera.cs. Sprite scaling calculation is only implemented for an orthographic camera.");
            return;
        }

        float worldScreenHeight = camera.orthographicSize * 2;
        float worldScreenWidth = worldScreenHeight / Screen.height * Screen.width;

        transform.localScale = new Vector3(
            worldScreenWidth / spriteRenderer.sprite.bounds.size.x,
            worldScreenHeight / spriteRenderer.sprite.bounds.size.y,
            1);
    }
}
8.1. Add ScaleToCamera script to the sprite renderrer to have the video stretch to full screen.
8.2. Drag the camera you created in step #7 to the Camera property in the ScaleToCamera component.

Hit the Editors play button you should see your video re-size to the game window and play.

Adding Play/Stop control

9. Turn off looping in the animation file that was automatically generated in step #5.
10. In the animator, add an "Idle" or "Stop" empty state. This is the state where the animation won't be playing.
10.1. Right click this state to set it as the default state.
10.2. Set this state to play the same animation file as the other state. 10.3. Set this state's animation play speed to 0. This makes the video always show only the first frame of the video when this state is active.
11. Add 2 Triggers to the animator; "Play" and "Stop"

12. Create a transition from "Idle" to the generated state using the condition "Play", uncheck exit time, set Transition Duration to 0.
13. Create a transition from the generated state back up to "Idle" using the condition "Stop", uncheck exit time, set Transition duration to 0

14. Create a second transition from the generated state back up to "Idle" leaving Exit Time checked, set exit time to 0.99, set Transition duration to 0

15. Add TransitionSprite.cs to the spriteRenderer
using UnityEngine;
using System;

[RequireComponent(typeof(Animator))]
public class TransitionSprite : MonoBehaviour
{
    /// <summary> The sprite animation has stopped playing, either from finishing or from running Stop() </summary>
    public event EventHandler<EventArgs> Finished;

    private Animator cachedAnimator;
    private bool debugToggle;

    void Start()
    {
        cachedAnimator = this.GetComponent<Animator>();
    }

    void Update()
    {
        // Note: this if block is used only for debugging purposes,
        if (Input.GetButtonDown("Fire1"))
        {
            debugToggle = !debugToggle;
            if (debugToggle)
                Play();
            else
                Stop();
        }
    }

    /// <summary> Plays the animation from the start to the end. </summary>
    public void Play()
    {
        cachedAnimator.ResetTrigger("Stop"); // clear stop to make sure the animation won't stop as soon as it starts.
        cachedAnimator.SetTrigger("Play");
    }

    public void Stop()
    {
        cachedAnimator.ResetTrigger("Play"); // clear play to make sure the animation won't play as soon as it stops.
        cachedAnimator.SetTrigger("Stop");
    }

    public void RaiseFinished()
    {
        Debug.Log("Finished playing " + this.name);
        if (Finished != null)
        {
            Finished(this, new EventArgs());
        }
    }
}
16. Add TransitionSpriteBehaviour.cs to the spriteRenderer's animator's generated state.
using UnityEngine;

public class TransitionSpriteBehaviour : StateMachineBehaviour
{
    // OnStateExit is called when a transition ends and the state machine finishes evaluating this state
    override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        var sprite = animator.GetComponent<TransitionSprite>();
        if (sprite != null)
        {
            sprite.RaiseFinished();
        }
    }
}

Now when you press the Unity3D editor's play button the video won't play until you click in the game window, clicking again will stop the video.
Here's the code above as a Unity project.

Labels:


Saturday, March 07, 2015

 

Interview at Nintendo in Redmond, WA

I recently had the honor of interviewing for a developer position at Nintendo in Redmond, WA. The first step was a 1 hour phone interview. They went over the details about the job. They also did an over the phone algorithmic coding exercise using http://www.codeshare.io as a shared session. Less than an hour afterwards I was informed that I succeeded at the 1 hour phone interview and was scheduled for both a half-day in-person interview and a 30 minute tentative follow-up interview. The in-person, half-day, interview: You start by talking to an HR rep for 10 minutes. Then you spend each hour with a different person. When they swap they confer privately with each other for a couple minutes. Each person asks you an programming question in a different manner; white board programming, heads down programming with internet, and paired programming with internet. The questions aren't that hard. Yet, personally, I had time issues. The white board programming seemed to focus around typical algorithmic interview questions. The other two were based on the tech requirements of the job description. Which for me was Xaml. So was working on binding to commands and collections stored in a ViewModel. The last interview was with the same individual that gave the phone interview. Talking a little bit about how things went with the previous 3 people and asking general programming questions. Some discussion about the next step, which is when you'll find out if you'll attend the tentative follow-up interview. Then you're seen to the door. This was the last step of the interview process for me. My "didn't make it" moment: I have a feeling they've had some incidents when telling people they feel they're not a right fit. As when the HR rep left a voice mail the next morning about if I should come into the tentative interview he used the words "he had to cancel". Which to me initially meant reschedule. Then I tried to call back, to no avail. I decided to email the Recruiting Coordinator, who nicely forwarded my email. After a couple hours I still didn't hear anything back from the HR rep and I realized "he had to cancel" must of meant "we don't think you're the right fit". In retrospect, it's likely the last person I saw during the in-person interview would of told me if it was positive news. Yet, being it was negative, he likely wanted to make sure I was out of the building. So, don't worry about it while they wait to call you back. If you're worried about losing some sleep, ask if you can schedule the tentative interview with them after the in-person interview is completed.

Labels: ,


Sunday, March 02, 2014

 

Getting started with StrangeIoC

StrangeIoC is a design pattern enabler for Unity3D projects. It focuses on decoupling logic from presentation through the use of Inversion of Control. This idea is summed up well in this old (2008) but well explained hour long video: http://www.dnrtv.com/default.aspx?showNum=126

StrangeIoC compared to MVVM in XAML

I come from a XAML background, where MVVM is the popular pattern. Some initial mapping of StrangeIoC to something I'm familiar with helped me get a grasp of its goals. This is how I found MVVM maps to StrangeIoC:
Unity3D game objects are the MVVM View, the XAML & Codebehind
StrangeIoC ContextView, Root GameObject & MvcsContext are the binding of the MVVM View to the ViewModel, aka XAML's DataContext
StrangeIoC View and Mediator is the MVVM ViewModel, what's bound to XAML DataContext
StrangeIoC Model/Service is the MVVM Model

How StrangeIoC gets started

The first hook into StrangeIoC is a top level GameObject from the Unity3D Scene Hierarchy. The \examples\Assets\scenes\myfirstproject\TestView.unity example that comes with strange shows this on the "ViewGO" gameobject with its MyFirstProjectRoot.cs component script. The purpose of this Root.cs script is to define and initialize a StrangeIoC context. This is explained with detail in the StrangeIoC How-To. I was looking for something with a little less detail to get started.

Building a Unity3D app with rudimentary usage of StrangeIoC

I made a top level Cube gameObject and attached a CubeRoot.cs script to it:
using strange.extensions.context.impl;
public class CubeRoot : ContextView
{
 void Awake()
 {
  context = new CubeContext(this); 
 }
}
The CubeContext is the mapping container for all StrangeIoC objects. It's not used correctly in the code below, as no mapping is done. This usage instead allows you to test that you have StrangeIoC included in your project correctly.
using UnityEngine;
using strange.extensions.context.impl;
using strange.extensions.context.api;
public class CubeContext : MVCSContext
{
 public CubeContext(MonoBehaviour view) : base(view)
 {
  view.renderer.material.color = Color.yellow; //verify view loads it's context
 }
}
You now have a completed basic framework for StrangeIoC. You can run the project. The cube should change to yellow by using its context.

Following a pattern by using injection

The constructor of the context class is not the right place for making modifications to the view. The purpose of the Context is to define mappings between the MVCS layers and to define the scope where GameObjects can send or receive events from other GameObject.
The initialization of a GameObject's modifiable properties should be pulled from a model and put into a view. There are views for many GameObjects inside a single context. This should also be represented in the Unity3D Hierarchy by making GameObjects with a view be children of a GameObject with a context. We need a context GameObject in the Unity3D Hierarchy. Our cube either needs a parent empty GameObject with a CubeContext.cs component or to become an empty GameObject and have a child Cube object. We'll also need a new CubeView.cs script. This is what our GameObject Hierarchy should look like:
The CubeContext will contain all the bindings for this Context. To create bindings in a context we need to overload the mapBindings() function of MVCSContext. This binding says that within this context, any property of type Color that has the [Inject] attribute will be initialized with the value Color.red.
using UnityEngine;
using strange.extensions.context.impl;
using strange.extensions.context.api;

public class CubeContext : MVCSContext
{
 public CubeContext(MonoBehaviour view) : base(view) { }

 protected override void mapBindings()
 {
  injectionBinder.Bind().To(Color.red); // map binding to a model
 }
}
For the sake of simplicity, I hijacked the Color datatype, using Color as our Model. The Color type would normally be replaced with a model interface and injected with a model that uses that interface. The CubeView makes use of the bindings defined in CubeContext.cs to inject the CubeColor from the model.
using UnityEngine;
using strange.extensions.mediation.impl;

public class CubeView : View
{
 [Inject]
 public Color CubeColor {get; set;}

 protected override void Start()
 {
  base.Start();
  this.renderer.material.color = CubeColor;
 }
}
There is much more to StrangeIoC than just these fundamentals. Understanding the flow above will make the StrangeIoC How-To easier to digest. There is also an introductory video on StrangeIoC in this article.

Labels: ,


Monday, June 10, 2013

 

Deploying to Android with Unity3D

Want to build Android games in C# or javascript without learning Java? Doing it in Unity3D is free. Yet getting Unity3D setup for Android deployments was tricky the first time I did it. It was even more of a challenge when I did it the 2nd time 4 months later. Hopefully these steps will make it easier for you or for when my third time comes around.

1. Download the Android SDK Tools, not the ADT Bundle! The SDK is shown under "use an Existing IDE". http://developer.android.com/sdk/index.html

2. Run the SDK Tools.
2.1 If the Android SDK Tools request it, install the 32bit version of the Java Platform Standard Edition JDK. (Installing only the 64bit version causes Unity Error: Error building Player: Win32Exception: ApplicationName='java.exe', CommandLine='-Xmx1024M...) http://www.oracle.com/technetwork/java/javase/downloads/index.html
2.2 You may need to manually set the JAVA_HOME Windows PATH variable https://confluence.atlassian.com/display/CONF26/Set+JAVA_HOME+variable+in+Windows

3. After the Android SDK has been installed be sure to start "SDK Manager.exe" from the installation folder.
3.1 Keep the defaults checked and check any additional Android versions that your older devices are running.
3.2 Click "Install ## packages..." in the lower right.
3.3 Accept the license and install. (It took me about 20 minutes for this to install 13 packages).
3.4 You do not need System Images to create an *.apk with Unity. However, dependencies for System Image packages are now complete, click "Install #packages..." again if you want to use the Android emulator.
3.5 [troubleshooting] If the Android SDK *.exe you downloaded contains r22 in it's file name (and you want to use Unity 4.1.3) you need to copy the contents of the /build-tools/17.0.0/ folder to the /platform-tools/ folder. This was suggested by "jvil" on http://forum.unity3d.com/threads/176737-unknown-error-when-building-simple-scene

4. Start up Unity3D and open Edit -> Preferences...
4.1 Click External Tools
4.2 Click Browse... next to "Android SDK Location"
4.3 Find the same folder that "SDK Manager.exe" is in from step #3 and click "Select Folder".
4.4 exit the Preferences window.

5. Click File -> "Build Settings..."
5.1 Make sure platform "Android" is selected.
5.2 Click "Player Settings..."
5.3 Find the Unity Inspector -> Per-Platform Settings -> Settings for Android -> "Other Settings".
5.4 Set the "Bundle Identifier" and "Minimum API Level".

6. Click File -> "Build Settings..." if you closed it.
6.1 Click Platform "Android".
6.2 Click "Switch Platform"
6.3 Click Build

Additional details for setting up the Android SDK to work with a device and creating a test application: http://blog.rabidgremlin.com/2012/03/10/tutorial-creating-your-first-unity-android-app/

Labels: ,


This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]