about         games         blog         contact
  • WIP Game
  • Cargo Delivery
  • Sally
http://www.catsinthesky.com/blog/article/2012/03/3/new-game-studies http://www.catsinthesky.com/game/cargo-delivery http://www.catsinthesky.com/game/sallys-cats

Introducing our newest Unity plugin, Texture Tools

Hello everyone,

We made a free Unity plugin in the past (FarseerUnity), and now we're happy to introduce our first commercial plugin, Texture Tools:

Texture Tools is a plugin that enables you to tweak some aspects of your textures inside Unity. All filters/tasks run in a separate thread, so they don't stop you from working on your game scenes.

The current version of our plugin contains:

  • Solidify - Removes white halos from transparent textures. Solidify fixes the fully transparent pixels of your textures so they can be displayed properly when they are sampled by the engine.
  • Resize - Lanczos (great for reduction), HQ2X, HQ4X and HQ4X Smooth (great for enlarging pixel-art textures).
  • HSL - Hue, Saturation and Lightness/Luminance. This can be useful if you're building for mobile and can't afford a custom shader due its performance cost.

http://www.catsinthesky.com/unity/texturetools

What is planned for future versions:

  • A simple texture editor for pixel-art painting and/or prototyping (multiple layers, brushes, etc.) [1.2.x];
  • Example scenes for using Solidify, Resize and HSL in-game (the filters don't depend on Editor classes, so they can be used for processing textures inside a game) [1.1.x];
(suggestions are welcome!)

FarseerUnity - Aftermath

The project is now at Unity Asset Store! One of the major updates is the settings panel. You can change all the Farseer Physics settings on a regular unity window. You can also set different settings based on the target platform.


Image 1 - FarseerUnity Settings

The performance was also tested many times so we could determine if it is actually possible to use FarseerUnity on a mobile device. The engine's bottleneck is the contact solver (image 2).


Image 2 - Profiler's bottleneck

There is nothing we can do to improve this function. This part of Farseer is already coded wisely, optimized in the best way it can be. The bad news is that it isn't running fast enough on mobile devices (well, we tested it on an iPad 1st gen), as you can see on the image below.


Image 3 - Running with FixedUpdate on iPad 1G

When it comes to performance on mobile hardware, what actually made a big difference when profiling on the iPad was to avoid FixedUpdate at all (this is now possible with the new settings window). The problem when using a variable time step is that the joints become more soft than usual, but that's the only way to keep a reasonable framerate on iOS at the moment. Using Unity's math classes didn't make a difference so we're keeping the FVector classes (it's easier to update Farseer this way).

Farseer Physics (Box2D) and Unity (Part #4)

Part #1 | Part #2 | Part #3 | Part #4 | .unitypackage | GitHub
This part of the tutorial covers collision events, a.k.a. triggers. Unlike collision filtering, triggers are always present in games. They are essential for a game's logic, from save points to deadly bullets.

Since triggers can be used for anything and in many different ways, I'll cover more the programming logic behind it (instead relying on already made components like in the previous parts). Grab the Unity package file, start a new project then import everything. The base scene is in Tutorials/Part 4/CollisionEventsBase (Image 1).


Image 1 - The tutorial starter scene

Now FarseerUnity also stores the Tag information inside Body and Fixture instances. You can use this to work with your trigger logic. For this tutorial, we will change the PLAYER tag to "Player" (Image 2) and the playerBOX tag to "Respawn" (Image 3).


Image 2 - PLAYER tag


Image 3 - playerBOX tag

Now, create a new C# script at Tutorials/Part 4/CollisionEventsBase folder named "TestCollisionEventsCp". For now we will leave it with this code:

using UnityEngine;

using System.Collections.Generic;

using FarseerPhysics.Dynamics.Contacts;

using FarseerPhysics.Dynamics;

using FVector2 = Microsoft.Xna.Framework.FVector2;

public class TestCollisionEventsCp : MonoBehaviour

{

}

Add this script to the PLAYER game object (Image 4).


Image 4 - The new component added to the PLAYER game object

Now replace the code with this basic structure:

using UnityEngine;

using System.Collections.Generic;

using FarseerPhysics.Dynamics.Contacts;

using FarseerPhysics.Dynamics;

using FVector2 = Microsoft.Xna.Framework.FVector2;

public class TestCollisionEventsCp : MonoBehaviour

{

private Body body;

private List<Contact> lastContacts;

void Start()

{

body = GetComponent<FSBodyComponent>().PhysicsBody;

lastContacts = new List<Contact>();

}

void Update()

{

}

private bool OnCollisionEvent(Fixture fixtureA, Fixture fixtureB, Contact contact)

{

return true;

}

}

The Farseer Physics Engine uses default C# events to dispatch when a collision takes place (body.OnCollision) and when it stops (body.OnSeparation). This is similar to Physx OnCollisionEnter and OnCollisionExit, but it is per fixture (a body can have multiple fixtures/shapes). To handle the in-between collision steps, you can store the contact instance, like it's prepared for the lastContacts variable.

After this line:

lastContacts = new List<Contact>();

Add this line:

body.OnCollision += OnCollisionEvent;

Now when a collision takes place, the OnCollisionEvent function will be triggered. It returns a boolean because the function tells Farseer if we want this collision to occur, or not. If you return false, the objects won't collide at all. Change the "return true;" into "return false;" and hit play on the editor to see what happens.

Now, to test a scenario when you don't want a collision between 2 specific objects (in this case, PLAYER and playerBOX), move the CubeB stack away from the player game object, and replace the OnCollisionEvent function with this:

private bool OnCollisionEvent(Fixture fixtureA, Fixture fixtureB, Contact contact)

{

Body bodyB = fixtureB.Body;

if(bodyB.UserTag == "Respawn")

return false;

return true;

}

Since we set the tags earlier, we can use them to filter a collision just like a simple collision filtering setup would do. If you hit play now, PLAYER and playerBOX won't collide because this event is manually filtering them out.

Now suppose you would want to react to this collision immediately. The Contact class holds everything you need to do that. The global Manifold holds the global collision points and the collision normal. Replace OnCollisionEvent with this:

private bool OnCollisionEvent(Fixture fixtureA, Fixture fixtureB, Contact contact)

{

Body bodyB = fixtureB.Body;

if(bodyB.UserTag == "Respawn")

{

FVector2 normal;

FarseerPhysics.Common.FixedArray2<FVector2> contactPoints;

contact.GetWorldManifold(out normal, out contactPoints);

bodyB.ApplyLinearImpulse(normal * -55f);

body.ApplyLinearImpulse(normal * 55f);

}

return true;

}

In the code above, the normal variable holds the global collision normal, so when we apply a force using it, the objects will fly at opposite directions. The global contact points could also be used for many things, such as playing 3D impact sounds at a point.

Suppose that playerBOX is a collectible item. You could do something like this:

private bool OnCollisionEvent(Fixture fixtureA, Fixture fixtureB, Contact contact)

{

Body bodyB = fixtureB.Body;

if(bodyB.UserTag == "Respawn")

{

// remove BodyB controller

Destroy(bodyB.UserFSBodyComponent.gameObject);

// add points, stats, anything else

// here

// don't return a collision

return false;

}

return true;

}

And for checking if the player can jump:

CODENEW

Now, let's do a more complex example to use what the Contact class can offer, like getting the total weight of an object (like scales). Since the contact also stores the resulting normal and tangent impulses, it's easy to do it. First, create a new 3D Text game object to display the weight (Image 5).


Image 5 - New 3D Text

Now make this game object a child of PLAYER (Image 6).


Image 6 - 3D Text GO as a child of PLAYER GO

And add a public member inside TestCollisionEventsCp class (paste the code below):

public TextMesh LinkedTextMesh;

Now drag the 3D Text game object instance to the LinkedTextMesh property (Image 7):


Image 7 - Linking the 3D text instance.

Now replace the TestCollisionEventsCp code with this:

using UnityEngine;

using System.Collections.Generic;

using FarseerPhysics.Dynamics.Contacts;

using FarseerPhysics.Dynamics;

using FVector2 = Microsoft.Xna.Framework.FVector2;


public class TestCollisionEventsCp : MonoBehaviour

{

public TextMesh LinkedTextMesh;

private Body body;

private List<Contact> lastContacts;

void Start()

{

body = GetComponent<FSBodyComponent>().PhysicsBody;

lastContacts = new List<Contact>();

body.OnCollision += OnCollisionEvent;

}

void Update()

{

float weight = body.Mass;

GetWeight(ref weight);

LinkedTextMesh.text = weight.ToString("#0.00") + "Kg";

}

private void GetWeight(ref float weight)

{

if(lastContacts.Count < 1)

return;

float ownWeight = weight;

weight = 0f;

foreach(Contact lastContact in lastContacts)

{

bool isTouching = lastContact.IsTouching();

if(isTouching)

{

FarseerPhysics.Common.FixedArray2<FarseerPhysics.Collision.ManifoldPoint> localManifoldPoints = 

lastContact.Manifold.Points;

// gravity = 9.8f (hard coded here just for testing purposes)

// Time.fixedDeltaTime is the FPE timeStep

weight += 

(1f * (localManifoldPoints[0].NormalImpulse/Time.fixedDeltaTime) / 9.8f);

weight += 

(1f * (localManifoldPoints[1].NormalImpulse/Time.fixedDeltaTime) / 9.8f);

}

}

// remove inactive contacts

for(int i = 0; i < lastContacts.Count; i++)

{

if(!lastContacts[i].IsTouching())

{

lastContacts.RemoveAt(i);

i = Mathf.Max(0, i - 1);

}

}

// calc weight

weight -= ownWeight;

weight *= 0.5f;

weight += ownWeight;

}

private bool OnCollisionEvent(Fixture fixtureA, Fixture fixtureB, Contact contact)

{

if(!lastContacts.Contains(contact))

lastContacts.Add(contact);

return true;

}

}

As you can see, the local Manifold points are packed with impulse data. This can be used to determine the total weight, crush force, break force, etc. To view the global manifold points and normals, add this:

void OnDrawGizmos()

{

if(lastContacts == null)

return;

foreach(Contact lastContact in lastContacts)

{

if(!lastContact.IsTouching())

return;

FarseerPhysics.Common.FixedArray2<FVector2> contactPoints;

FVector2 normal;

lastContact.GetWorldManifold(out normal, out contactPoints);

Vector3 p0 = FSHelper.FVector2ToVector3(contactPoints[0]);

Vector3 p1 = FSHelper.FVector2ToVector3(contactPoints[1]);

Vector3 pn = FSHelper.FVector2ToVector3(normal);

Gizmos.color = Color.red;

Gizmos.DrawWireSphere(p0, 0.15f);

Gizmos.DrawLine(p0, p0 + pn * 2f);

Gizmos.color = Color.yellow;

Gizmos.DrawWireSphere(p1, 0.15f);

Gizmos.DrawLine(p1, p1 + pn * 2f);

}

}

If everything is working properly, the weight will change when you stack objects on top (Image 8):


Image 8 - 44.0Kg

That's it for part 4! I decided to focus on programming because it doesn't matter how many collision events components I may add in the future, there will always be a case when your game needs something specific.

Farseer Physics (Box2D) and Unity (Part #3)

Part #1 | Part #2 | Part #3 | Part #4 | .unitypackage | GitHub
Now that we have the basics, concave objects and joints, what about collision filtering? Eventually you will reach a development stage in your game that "certain objects should not collide with each other", or "Group A" should ignore collisions from "Group B". That's when Collision Filtering kicks in.

I like the way that Farseer Physics Engine extends this feature from Box2D. Instead of setting hard-coded bit flags on each fixture, you just need to set collision groups. A fixture can belong to zero or more groups and it can also collide with any group(s) you want to. When I was porting this to Unity, I avoided using Unity layers and tags because both properties are used a lot on other things, but it also couldn't be a DIY hard-coded solution.

Now, there's a new configuration window. Right now there's only the option to name the collision categories, but I plan to port all settings defined in Farseer Physics Settings.cs file (so you don't have to change them in code).


Image 1 - New configuration window

I also decided not to use PlayerPrefs (and EditorPrefs) for the configuration files, since the PlayerPrefs can't be easily shared between developers, and any end user can edit them, so it's unsafe. The configuration is compiled with all the other scripts.


Image 2 - Configuring collision categories

To begin with this tutorial, download the latest package then open the scene indicated on the image below (Image 3).


Image 3 - Base scene

For convenience's sake, the collision groups are already setup. They are located at Assets/Tests/CollisionGroups. To create a new group, right-click at a folder then go to Create/FarseerUnity Collision Group (Image 4).


Image 4 - New collision group asset

The Collision Group asset is pretty simple. It's just a bunch of checkboxes that you can determine the category (or categories) this group belongs and what collides with it (Image 5). This process saves you from doing this setup on each shape.


Image 5 - A Collision Group asset

Now, inside the "STATIC" GameObject, there are 3 GameObjects that are the ground and walls of the scene, go to the "filter Collision" property of the FSShapeComponent and change it to "Preset File" (Image 6).


Image 6 - Preset file popup field

Drag the "Ground" Collision Group asset to the object field (Image 7).


Image 7 - A wild object field appears

Now, inside the "complexBody" GameObject there's a FSConcaveShapeComponent that also needs to be setup (Image 8). Drag the "RegularObjects" collision group to it.


Image 8 - Setting up another collision group

Don't forget to click on the "Generate convex shapes" button (since you altered its properties).


Image 9 - To update the convex shapes

Setup the "Sphere00" to use the "Oranges" group, and the "Cube" to use the "Rectangles" group. Then duplicate all the dynamic objects several times to test the collision filtering, then press play!


Image 10 - Testing the scene

That's it for part 3. The next part of the tutorial will cover collision events.