Unity Drawing a Sprite on a Camera Plane
For our game, Verdant Skies, nosotros wanted it to have a traditional hand drawn look, but we also wanted to make it more interesting by giving it a full 3D perspective and adding effects such every bit shadows and water reflections. This fit the skills of our small-scale team well.
On older, 2d hardware, information technology would certainly be possible to achieve a similar look simply by controlling the drawing lodge, calculating the position and scale of each sprite. Since we were using a 3D engine (Unity) we might as well salve ourselves some trouble! With the default settings, Unity won't assist you lot brand a projection similar this, and there are a few issues. First of all, Unity doesn't really know how to depth sort sprites in a scene like this. Secondly, the projection needed is not a basic perspective like the one provided by Unity, and it can await quite bad if you use one. However, with the right settings and a lilliputian matrix math, it is possible to get a archetype project without a lot of work. For an case, attempt our interactive WebGL demo. A link to our camera script is included at the end of the article in the resources section.
Drawing Order
The starting time effect is how to draw the sprites in the correct order. Since most 2D games use blastoff blending, Unity must draw the sprites in the correct social club without the do good of z-buffers. Unity's sorting support is somewhat limited for 2D games, and people have tried a lot of creative, though boring attempts to piece of work around it. Fortunately, though with a combination of several features a simple solution exists.
Since Verdant Skies uses a 3D perspective, it wasn't possible to utilize z-value exclusively for depth sorting. Moving objects forth the z-axis would change their size and pause their reflections and shadows. Also, because Unity depth sorts sprites by their center, we couldn't rely on the camera to sort Verdant Skies' sprites automatically either as some sprites laid on the ground and others stand up upright. If the player walked past the heart of a patch on the ground, their sprite would disappear underneath the ground! Using sorting layers and orders isn't ideal, because it requires updating the sorting club every time an object moves. Besides, the sorting order belongings provides very picayune precision (but sixteen bits), which limits the size of the world.
Instead, we use sorting layers to draw sprites from the footing upwardly, and utilise the orthographic camera sorting mode to depth sort sprites within the same layer. The basic ground patches were drawn outset, and so the detail patches on top (gravel, fields, etc), and then finally the sprites that stood upright. Since ground patches are always apartment, plants and characters are ever upright, etc, then the sorting layer never changes and becomes a unproblematic matter of setup.
The remaining task is to sort sprites within the aforementioned layer. This actually but matters for upright objects, and we want them to be sorted automatically by their depth in the scene. Orthographic cameras already default to the correct sorting mode. Nonetheless, for perspective cameras the default sorting way compares objects by their altitude to the camera's center. This doesn't work with sprites since moving the camera left or right will cause them to pop in front on one another. Information technology's especially noticeable for large sprites like buildings or trees and can be very distracting. Fortunately, you can configure your perspective camera to sort by depth like orthographic cameras practice. You tin can simply configure your camera in a Start() method.
camera.transparencySortMode = TransparencySortMode.Orthographic; Put together, the sorting layer draws sprites from the footing upward and the photographic camera's depth sorting draws them from front to back. All of the sorting will happen automatically without needing to intervene with your own scripts.
2nd Projections
The side by side job is to render the correct projection. Consider screenshot to a higher place equally the goal. The sprites are all drawn parallel to the screen without any distortion. The ground is viewed from a 30° angle with a sixty° FOV to give information technology some subtle perspective. (This is difficult to encounter in a static image, but try the demo higher up.) Ideally it should be possible to able to achieve this look without writing a lot of code, or irresolute the all the objects in the scene to make the camera work.
The almost obvious solution is probably to effort a perspective camera, but the screenshot higher up shows the problem. Fifty-fifty with a small 60° field of view, the camera is looking straight down on the sprites nigh the bottom of the screen, revealing their paper thin being. On the other hand, the trees nigh the top do look ok.
An orthographic projection doesn't fare much ameliorate. Although it does a better job hiding that the art is only a flat facade, it now distorts all of the sprites the same amount and they all look squished! A potential ready for both of these basic projections is that if we tilted all of the sprites then they pointed at the photographic camera, then it would probably look good. In fact, that is exactly what the ready is. Since tilting every sprite in every scene would exist ho-hum, math to the rescue!
In all of these cases, nosotros are looking at mappings from 1 coordinate space (3D coordinates) to another (screen coordinates). That is the essence of a projection, and at that place are quite a few useful ones other than the two that Unity provides. You've likely heard of 2 or three signal perspective, orthographic, and isometric projections. These are examples of linear perspectives, but there are too plenty of non-linear projections too, such as fisheye lenses and all of those crazy map projections yous've seen of the Earth.
Since realtime 3D graphics is very heavily based effectually matrices, it's perhaps no surprise then that the two bones projections offered by Unity can be represented past a matrix. In fact, any linear projection can be reduced to a matrix, so the interesting question to ask is which projections are linear. The easiest way to think about it is: if something is a straight line in the world, then it will be a straight line in the project. That rule works both ways too, so any straight line on the screen is projected from a directly line in the globe also. Given the right matrix, we should exist able to get Unity to return any linear projection nosotros want, even Ultima's unique (and sometimes despised) oblique projection!
While this commodity is virtually fixing Unity's projection for 2D content, it also works with 3D content, and is used for similar reasons. In fact, A Link Between Worlds used this same technique to more closely match the look of older 2D Zelda games. The game was even rendered in stereoscopic 3D without causing any obvious distortion for the viewer.
(via Zelda Dungeon)
Using Custom Projections with Unity
In basically all 3D engines, a matrix is used to transform objects from the world onto the screen. This matrix is called the model-view-projection matrix, and is broken into 3 parts. First, the model matrix transforms from model coordinates to earth coordinates (Transform.localToWorldMatrix). Each object gets its own model matrix that positions information technology relative to other objects based on the its transform. Side by side, the view matrix transforms globe coordinates to be relative to the camera (Camera.worldToCameraMatrix). Finally the project matrix transforms those camera relative coordinates into screen coordinates (Photographic camera.projectionMatrix). The view and projection matrix are shared by all objects in a scene and decide the overall projection to the screen. Unremarkably, the projection matrix is only responsible for choosing between an orthographic or perspective projection and the overall scale of the scene as rendered by the camera. Recollect of it like a camera's lens.
To prepare upward a custom projection, offset choose if you want an orthographic or perspective view. The projection matrix handles that part, and you can employ Unity's regular Photographic camera inspector to ready information technology up. The direction each axis points relative to the screen are then handled past the view matrix. Ordinarily, Unity updates this matrix every frame based on the camera's transform, only writing to the Camera.worldToCameraMatrix property overrides it with a custom value. Since we are okay with how the ground looks, nosotros don't need to modify the output for the x or y-axes, and only need to change the z-centrality. Specifically, we desire the earth's up direction to ever concord with the camera's up direction. Since each cavalcade of the matrix corresponds to an axis, we just have to change the column for the z-axis. The code for that would look something like the following.
private void OnPreCull(){ // First summate the regular worldToCameraMatrix. // Showtime with transform.worldToLocalMatrix. var m = camera.transform.worldToLocalMatrix; // And so, since Unity uses OpenGL's view matrix conventions // nosotros have to flip the z-value. m.SetRow(2, -grand.GetRow(2)); // At present for the custom projection. // Ready the world'south up vector to always align with the camera'due south up vector. // Add a small amount of the original upwards vector to // ensure the matrix will be invertible. // Try irresolute the vector to see what other projections you tin become. thou.SetColumn(2, 1e-3f*m.GetColumn(2) - new Vector4(0, 1, 0, 0)); camera.worldToCameraMatrix = grand; } The best place to set the view matrix is in the camera'due south OnPreCull() consequence method. This is called after all of the variations of the update methods, but before any of the rendering work begins.
User Input Coordinates
A game that uses mouse or touch input volition probable need to convert screen coordinates into world coordinates. In a basic second game, Photographic camera.ScreenToWorldPoint() would suffice, only when using a custom projection, it becomes more complicated. While information technology's possible to use existing Unity APIs to construct a ray and bank check it'due south intersection with the scene, if all you have is a footing airplane, then at that place is a simpler mode. Writing your own version of ScreenToWorldPoint() is merely a few lines of lawmaking, and with but one more, you can make it work with coordinates on the ground plane. The bones idea is that Unity uses the view-projection matrix to convert world coordinates to the screen, so using the inverse of that matrix we tin convert screen points to the globe. By changing the matrix to ignore the z-centrality, it'south possible to ignore the scene depth and only go points on the ground aeroplane.
public static Matrix4x4 ScreenToWorldMatrix(Camera cam){ // Brand a matrix that converts from // screen coordinates to clip coordinates. var rect = cam.pixelRect; var viewportMatrix = Matrix4x4.Ortho(rect.xMin, rect.xMax, rect.yMin, rect.yMax, -1, one); // The camera's view-project matrix converts from globe coordinates to clip coordinates. var vpMatrix = cam.projectionMatrix*cam.worldToCameraMatrix; // Setting column 2 (z-axis) to identity makes the matrix ignore the z-axis. // Instead you become the value on the xy plane! vpMatrix.SetColumn(2, new Vector4(0, 0, one, 0)); // Going from correct to left: // convert screen coords to prune coords, so clip coords to earth coords. return vpMatrix.inverse*viewportMatrix; } public Vector2 ScreenToWorldPoint(Vector2 point){ return ScreenToWorldMatrix(camera).MultiplyPoint(point); } Splitting this conversion into two methods provides flexibility to cache the matrix to convert many points in a single frame.
Custom Projections for the Scene Editor
So with a minimal corporeality of code, it'southward possible to have Unity render a custom project similar to ones used in classic 2d video games complete with support for perspective, input, and automatic draw social club sorting. The last obstruction to tackle is how to become the custom projection working with the Unity scene view for WYSIWYG editing.
Fortunately, contempo versions of Unity provide the hooks needed to make this work. Beginning, the photographic camera script will need to have the [ExecuteInEditMode] attribute, otherwise it will only work when Unity is in play mode. The following code, builds on the OnPreCull() lawmaking listed above.
private void OnEnable(){ // Optional, only enable the callbacks when in the editor. if(Awarding.isEditor){ // These callbacks are invoked for all cameras including // the scene view and camera previews. Photographic camera.onPreCull += ScenePreCull; Camera.onPostRender += ScenePostRender; } } private void OnDisable(){ if(Awarding.isEditor){ Camera.onPreCull -= ScenePreCull; Photographic camera.onPostRender -= ScenePostRender; } } private void ScenePreCull(Camera cam){ // If the photographic camera is the scene view camera, call our pre cull method. if(cam.cameraType == CameraType.SceneView) OnPreCull(); } private void ScenePostRender(Camera cam){ // Unity's gizmos don't like it when you change the worldToCameraMatrix. // The workaround is to reset it subsequently rendering. if(cam.cameraType == CameraType.SceneView) cam.ResetWorldToCameraMatrix(); } This provides a useful, though imperfect editing experience in the scene view. Though you tin edit an object in the inspector only fine, some of the widgets in the scene view are incorrect. In particular, transforms with an offset on the z-axis won't draw at quite the right places, and rect transform handles will exist facing on the incorrect axis. Some of this can exist fixed by disabling the onPostRender event, but it causes dissimilar issues. Information technology may non be possible to accept a perfect editing experience, though you can notwithstanding have an improved 1.
Conclusion
So with a little bit of extra matrix math, you can save yourself a lot of problem. Instead of hacking a semi-2d projection on tiptop of a 3D engine, become the engine to do information technology for you. Every bit a bonus, a lot of other effects such as reflections and shadows are simple variations. In Verdant Skies, we take two extra cameras that render the scene with dissimilar projections. Reflections simply point the up vector downwards on the camera, and shadows point it in the direction of the shadows on the basis.
Resources
CustomProjection.cs for Unity
http://www.zeldadungeon.net/2013/eleven/iwata-asks-a-link-betwixt-worlds-perspective/
Source: https://www.gamedeveloper.com/programming/adding-some-perspective-to-your-unity-2d-game-
Postar um comentário for "Unity Drawing a Sprite on a Camera Plane"