I’ve been playing quite extensively over the past few weeks with Unity3D and I’ve realised that it is quite a powerful and flexible environment.
I’m in the process of constructing a game for iOS which involves (without giving details away), moving an object into a specific place, but without giving you direct visual access to it. Your placements are done with the aid of an ‘X-Ray style photography’, and you get points for the fewest amount of shots taken to place the object.
Here are the steps I took to develop this:
- Set up Scene
- Set up ‘X-Ray’ camera
- Create a GUI Button
- On Button Press, Record X-Ray
By far the most complex part of this is the final one. I’ll describe my scene as detailed as possible to allow you to get an idea of how the scene is constructed, then I’ll talk about how to record the ‘X-Ray’.
The Scene
I created a scene, simple to start with, whilst I created the functionality, then I can step up the complexity of the visuals.
First, I created a plane at position, 0,0,0 and scale 10,10,10, with a single point light at 0,20,0 with a range of 50 and an appropriate intensity.
Onto this plane, I placed a Cube at position 0,0,0 with Scale 2,2,2. Next I placed a Sphere at position 0,0.5,0 with scale 1,1,1. This meant that my Sphere was smaller than my Cube and was invisible, because it was situated within the Cube.
I textured these for clarity; a bog standard dark grey Diffuse texture for the Cube, but a light purple coloured Diffuse for the Sphere.
I created a Tag for the cube, since I wanted to identify this object as being ‘semi-permeable’ to my camera once I code it. The tag was ‘XRay’
The X-Ray Camera
Aside from my Main Camera, I created a second one, positioned at 0,10,0, with a field of view of 10 and a rotation of 90 on the X. This meant the camera was looking directly down upon the objects and the viewport slipped to the plane. To this Camera I added a Spot Light which ranged to the plane.
This camera and it’s light are marked as disabled for now.
The GUI Button
I created a simple GUITexture Object and added a button GUI Texture to it. The Texture was of type GUI and it was Uncompressed (True Colour) to avoid any low quality-ness when I drop it onto iOS.
Next, I was able to create a button script to handle the tapping – I’ll leave this part out, to focus on the screenshot processing, but to summarise, I check for number of Input Touches and in a for loop, check to see if any of the touches fall within the bounds of the GUI Texture, and that the Touch has passed it’s ‘Ended’ phase. Then I execute a piece of code to take the X-Ray.
Also of note, I have a GUITexture Object in the top-right hand corner which currently shows a plain white square.
The X-Ray
I can see the need for some very strict stages in taking the screenshot.
- Re-texture all the items with the XRay Tag to be semi transparent
- Enable the Second Camera & it’s Light
- Ensure Rendering has completed for this frame
- Take a Screenshot and Save it as a Texture
- Apply the new texture to our Top-Right Hand Corner GUI
- Return everything to normal
If we do this right, then we’ll only see a brief flash of activity and the deed will be done.
To retexture all items with the X-Ray tag, I created a convenience function. This convenience function depends on a public Transparent Diffuse texture, made available to the code.
I will make this texture semi-transparent in order to see through it somewhat.
I also need to save any existing textures into an array, in order to re-apply them later.
This convenience function depends on a direction variable which tells us whether we’re switching to or from the X-Ray Materials.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
var xRayMaterial : Material; private var xRayTag : String = "XRay"; private var originalMaterials : Array = new Array(); function RetextureAllItems(direction){ var listOfObjectsToBeXRayed = GameObject.FindGameObjectsWithTag(xRayTag); var i : int = 0; for(var object : GameObject in listOfObjectsToBeXRayed){ if(object.renderer.isVisible){ var material : Material = object.renderer.material; if(direction==1){ material = originalMaterials[i]; object.renderer.material = material; } else { originalMaterials[i] = material; material = xRayMaterial; object.renderer.material = material; material.color.a = 0.4f; } } i++; } } |
Okay, a quick explanation.
Get all X-Rayable Objects, loop through each to first determine if it is within the camera’s view. If the direction is -1, i.e. making it an X-Ray, save each material into an array and swap it for the new texture, giving it a 0.4 transparency. If the direction is 1, i.e. returning to normal, read the saved material from the array and reapply it to the object.
Simple.
Now all I have to do is specify
RetextureAllItems(-1) or
RetextureAllItems(1) in my code and voila, they are re-textured and back again.
Now, back to my main process.
|
1 2 3 4 5 6 7 |
//prepare the XRayCamera RetextureAllItems(-1); xRayCam.enabled = true; xRaySpot.enabled = true; //wait for rendering to complete yield WaitForEndOfFrame(); |
Call my Retexturing Convenience Method to re-texture all X-rayable materials in the scene. Next, enable the camera and it’s light, the wait for rendering to complete.
Now for the fun bit; The Screenshot.
|
1 2 3 4 5 6 7 8 |
//take screenshot and save as a texture var width = 303; var height = 280; var tex = new Texture2D( width, height, TextureFormat.RGB24, false ); tex.ReadPixels(Rect((Screen.width/2)-(width/2), (Screen.height/2)-(height/2), width, height),0,0); tex.Apply(); //apply new texture to the XRayReceiver receiverTexture.guiTexture.texture = tex; |
This code looks at specified margins (in my case 303×280) and takes a screenshot of it from the centre of the screen.
Then this is made into a texture and it’s applied to my receiever (the square in the top-right).
Now we just return everything to normal:
|
1 2 3 4 |
//return game state to normal xRayCam.enabled = false; xRaySpot.enabled = false; RetextureAllItems(1); |
As mentioned, my convenience Retexturing method returns all the textures back to their original state.
A complex process but relatively easy to actually implement.
I hope this works for you as it did me.
Comments
Powered by Facebook Comments