Category Archives: categories

Animation test, finally!

So in the last post I covered the easy model creation pipeline I was working on. Since then, I solved the final piece of that puzzle which is to automatically generate the rig. All of that is rather boring so I’ll save it for another day. Today, I want to show the point of all that, which is to animate! I got two animations setup, east idle and east run. Check it out!

I’ll fill out the north, south and west (maybe mirror east?) and then post some more videos.

Easy model creation

In the skeletal animation experiment I created the model by hand, placing one polygon per pixel using an image plane as a template and then manually setting the UVs to match up with the sprite. That was way to tedious and definitely not scalable. I wrote a handy little python function that will take a sprite as input and do all the work for me :-). Check it!

I think I’ll still need to create the “rig” manually. There will be one unique rig per direction, with east/west potentially just being a mirror. After I create one decent quality animation set and I automate the rigging process then I am confident that this will be my pipeline.

Skeletal animation experiment

One of the next things I want to figure out is how I’ll handle the character animations. I’ve been using the tiny dungeon sprites from Oryx Design Lab, and I had been thinking that skeletal animation might help bring them to life in an interesting way. I didn’t quite know how I would do this so I did some research and there are a ton of options to try out, from Creature to Spine2D, to Maya, and even Unity itself supports animation from within the editor. Here is my first test:

I brought the 16×16 sprite into Maya and slapped it onto an image plane, scaling it up 100 times until each pixel was 1 grid unit, and then I made a 1×1 polygon plane for each pixel. I ended up using a planar projection and using the sprite as a texture, but I think it may be more efficient to either use vertex colors or a tiny 1D texture that has the 8 colors the sprite has in it.

This animation is just a simple waving idle, so I want to do another test with more action like a run or an attack. I like where it is going so I want to see if I can take it to the limit as well, with FX and everything. Then I want to see if I can streamline the setup process by automating the manual steps I did to turn the pixels into polygons. Cheers!

Movement and dodging experiment

The game has reached a mini-milestone. All of the parts I need to test movement and dodging projectiles are finished. Like many Roguelike games, each move is a turn and all characters take turns simultaneously, but at different rates. What I wanted to test here is the turn based movement of the Player and the continuous movement of projectiles. The combination of these requires the Player to move in order to dodge.
I’ll be throwing builds into my public Dropbox folder if anyone wants to playtest and give feedback. Next up are the damage system and I want to try another experiment, which is the use of skeletal animation.

Yet unnamed Roguelike

While my wife was at GDC I began work on a Roguelike game in Unity.  I have limited time, so progress has been slow but steady.  I’m currently using art from Oryx Design Lab (http://ift.tt/1iD1s45). I’m close to the point where I can playtest the most basic mechanics.  I’ve definitely suffered from the ‘start a lot of projects that don’t get finished’ problem in the past and I am hoping that keeping a devlog will help push me to ship this game by making my progress public.
Honestly I don’t have a super strong ‘seed’ that everything is growing from.  I am definitely figuring things out as I go.  My current thinking is that the game will have these elements
  1. Movement is turn based like most roguelikes
  2. Projectile movement is continuous, so you’ll have to move to dodge them
  3. I’ll have a pixelated art style, probably 16×16 for most characters and environment tiles, however the characters will use skeletal animation
  4. It will be the inverse of Rogue Legacy in that, you venture outside of a castle into a new environment after each death
  5. There will be weather and survival elements like Unreal World, but less so.  
  6. Your progress is measured in how many days you survive, like in Neo Scavenger
I don’t know much more than that right now.  I’m very close to being able to test #2 from above
This blurb and this image are what sparked the whole thing:
Game takes place across a series of ‘single screen’ battles, al la OG Zelda.
As you move to the edge of the screen, the camera lerps to the next screen
     crazy town idea–it is multiplayer, you get matchmade with players in the next screen
Turn based, roguelike style movement of characters
Projectiles can be dodged in real-time
reverse of rogue legacy–where instead of going into a randomly generated castle, you exit your home into the randomly generated forest
     you only get to keep what you bring back?
     you only get to keep ‘special’ items a la enchanted cave 2
Here is my current todo list
Stay tuned for more updates!

Unity3D AssetPostprocessor Example

In Unity3D, you’ll often find yourself repeating a lot of simple tasks on prefabs if you make changes to the source content after you’ve placed instances of the prefab in the editor; stuff like turning off the renderer for some meshes, or adding colliders and scripted components. Unity provides a mechanism to automate much of this work with the AssetPostProcessor class.

In your Maya scenes, every mesh, bone, etc are all GameObjects in Unity. When you save your Maya scenes, it triggers them to be re-imported in Unity and when that happens, the AssetPostProcessor’s OnPostprocessGameObjectWithUserProperties() method will get called once for every GameObject in the Maya scene. If any of those GameObjects have custom attributes added to them, each attribute will have its attribute name and attribute value in the arrays passed into OnPostprocessGameObjectWithUserProperties.

Ok. So what is all this code? The examples I have seen usually have something like this inside the OnPostprocessGameObjectWithUserProperties method:

if (userPropertyName == "AddMeshCollider")
{
    ...
}
else if (userPropertyName == "AddSphereCollider")
{
    ...
}

There is nothing wrong with that code, but my preference is to separate out the logic that handles what to do for each user property into its own class, and never touch the OnPostprocessGameObjectWithUserProperties method again.

To do that, we can use the Attribute class and C# reflection. We’ll create an ImportTaskAttribute that takes a name in its constructor. That name will correspond to the user property name (the custom attribute name in Maya.)

All classes that are marked up with that attribute will get collected by an ImportTaskCollector class which we’ll use to fetch them and then call their Execute method to do the work.

using System.Linq;
using UnityEngine;
using UnityEditor;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

This is the ImportTaskAttributeClass. The TaskName property will hold the name matching the user property.

[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class ImportTaskAttribute : Attribute
{
    public string TaskName;
    
    public ImportTaskAttribute(string taskName)
    {
        TaskName = taskName;
    }
}

This interface is the contract for all of the import tasks. And the concrete class below is what we’ll inherit from.

public interface IPostProcessGameObjectWithUserPropertiesTask
{
    GameObject ImportedGameObject {get; set;}
    string UserPropertyName {get; set;}
    object UserPropertyValue {get; set;}
    
    bool ExecuteTask();
    void PostExecuteTask();
}

public class PostProcessGameObjectWithUserPropertiesTask : IPostProcessGameObjectWithUserPropertiesTask
{
    public GameObject ImportedGameObject {get; set;}
    public string UserPropertyName {get; set;}
    public object UserPropertyValue {get; set;}
    public object UserData {get; set;}
    
    public virtual bool ExecuteTask() { return false; }

    public virtual void PostExecuteTask() {}
}

This is the ImportTaskCollector. Its job is to find all classes marked up with the ImportTaskAttribute.

public sealed class ImportTaskCollector
{
    private Dictionary<string, object> _importTasks = new Dictionary<string, object>();
    
    public ImportTaskCollector()
    {
        Assembly assembly = typeof(IPostProcessGameObjectWithUserPropertiesTask).Assembly;
        Type target = typeof(IPostProcessGameObjectWithUserPropertiesTask);        
        var importTaskTypes = assembly.GetTypes()
                            .Where(type => target.IsAssignableFrom(type));

        foreach (Type importTaskType in importTaskTypes)
        {   
            if (importTaskType.IsAbstract || importTaskType.IsGenericType)
            {
                continue;
            }
 
            var importTaskInstance = Activator.CreateInstance(importTaskType);
            var importTaskTypeAttributes = Attribute.GetCustomAttributes(importTaskType);

            var importTaskTypeAttribute = importTaskTypeAttributes
                .SingleOrDefault(attr => attr as ImportTaskAttribute != null) as ImportTaskAttribute;
            
            if (importTaskTypeAttribute != null)
            {
                _importTasks.Add(importTaskTypeAttribute.TaskName, importTaskInstance); 	
            }
        }
    }
    
    public IPostProcessGameObjectWithUserPropertiesTask GetTask(string taskName)
    {
        if (_importTasks.ContainsKey(taskName))
        {
            return _importTasks[taskName] as IPostProcessGameObjectWithUserPropertiesTask; 
        }
        else
        {
            return null;
        }
    }
}

Finally, this is our custom AssetPostprocessor.

public class AssetImport : AssetPostprocessor
{
    private readonly ImportTaskCollector _importTaskCollector;
    
    public AssetImport()
    {
        _importTaskCollector = new ImportTaskCollector();
    }
    
    void OnPostprocessGameObjectWithUserProperties(GameObject g, String[] userPropertyNames, System.Object[] userPropertyValues)
    {
        for (int propertyPairIndex = 0; propertyPairIndex < userPropertyNames.Length; propertyPairIndex++)
        {
            var userPropertyName = userPropertyNames[propertyPairIndex];
            var userPropertyValue = userPropertyValues[propertyPairIndex];
            
            IPostProcessGameObjectWithUserPropertiesTask importTask = _importTaskCollector.GetTask(userPropertyName);
            
            if (importTask != null)
            {
                importTask.ImportedGameObject = g;
                importTask.UserPropertyName = userPropertyName;
                importTask.UserPropertyValue = userPropertyName;
                
                if (importTask.ExecuteTask())
                {
                    importTask.PostExecuteTask();
                }
            }
        }
    }
}

Ok, with all of that in place, here is an example of how you would use it:

[ImportTaskAttribute("AddMeshCollider")]
public class AddMeshColliderTask : PostProcessGameObjectWithUserPropertiesTask
{
    public bool ExecuteTask()
    {
        ImportedGameObject.AddComponent<MeshCollider>();
        return true;
    }
}

Then, if you add an attribute named “AddMeshCollider” to any mesh in your maya scene, your task will get run for that game object and a MeshCollider will be added.

But we can do better. We can make a generic AddComponentTask where the generic type parameter is the Component we want to add.

public class AddComponentTask<T> : PostProcessGameObjectWithUserPropertiesTask where T : Component
{
    public override bool ExecuteTask()
    {
        UserData = ImportedGameObject.AddComponent<T>();    

        return UserData != null;
    }
}

[ImportTask("AddMeshCollider")]
public class AddMeshColliderTask : AddComponentTask<MeshCollider>
{
	// Nothing to do, ExecuteTask() gets called on AddComponentTask<T>
	// and T is MeshCollider in this case.
}

Now, what if we wanted to add a CapsuleCollider and then set the height and radius from within Maya? The UserData property holds the component we just added, and now that code lives in the AddComponenTask‘s Execute method. We don’t want to keep duplicating the code for adding a component, so this is where the PostExecute method comes into play.

[ImportTask("AddCapsuleCollider")]
public class AddCapsuleColliderTask : AddComponentTask<CapsuleCollider>
{
	public override void PostExecuteTask()
	{
		var capsule = UserData as CapsuleCollider;
		var capsuleData = UserPropertyValue as Vector2?;
			
		if (capsule != null && capsuleData.HasValue)
		{
			capsule.height = capsuleData.Value.x;
			capsule.radius = capsuleData.Value.y;
		}
	}
}

As always, if you’ve got any questions, shoot me an email at jspataro@gmail.com