The Demo

Here’s a CSS3 demo of the Donut Get! background.

Use your mouse to adjust the perspective of the scene.

I’ve been messing around with CSS3′s 3D Transform ability recently. I was looking for a way to achieve some primitive 3D visuals strictly with HTML. I wanted something that would also run on phones and tablets.

The Alternatives

WebGL, although powerful, didn’t seem to be a good option since it’s not really an accepted standard at the this point, as much as enthusiasts would like it to be. I considered messing around with three.js as it has canvas functionality that could function as a fallback if WebGL wasn’t available. Before trying that out though, I discovered CSS3′s 3D Transform abilities and how easy it was to work with.

I came across some demos with Sprite3D.js that ran well on my iPad so I thought I’d look into CSS3 stuff. I discovered that Sprite3D was just a simple wrapper for CSS3 transform so I just decided to jump into it for myself.

The Setup

The beauty of this is that you can setup all your game elements as divs. I never thought of using divs as game objects until the homie Michael Anthony paradigm shifted my perspective a bit. Using javascript, we can update the position of the player, enemies, etc. using CSS properties like ‘left’ or ‘top’ or in the case of this demo, ‘translate3d.’

Here’s all the important HTML I used to setup the scene:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="SokayAppView">
    <div class="GameWorld">
        <div class="DonutGetSkyline"> </div>
        <div class="DonutGetMid"> </div>
        <div class="DonutGetShoppe"> </div>
        <div class="DonutGetBuildingLeftSide"> </div>
        <div class="DonutGetBuildingRightSide"> </div>
        <div class="DonutGetSidewalk"> </div>
        <div class="DonutGetBuildingLeft"> </div>
        <div class="DonutGetBuildingRight"> </div>
    </div>
    <div class="GameBGSky">
    </div>
</div>

The “SokayAppView” and “GameWorld” elements are used to create a “frame” for the content to live in. In this example I set the dimensions to 480×300 to fit within this blog, but you can easily set it to 100% or whatever you like. Each of the game elements are defined as divs within “GameWorld.”

The order I listed the divs is just to control the order the elements are displayed in. When using translate3d, the browser still uses the Z-index to control the display order unless you set the property to “transform-style: preserve-3d;”, which will make the objects display by their actual Z position in space.

The CSS

I’m using the “GameWorld” container to control the perspective of the scene. From my understanding, the settings you set in this parent element will affect all the children which are set to “preserve-3d.” Something like that… haha! Changing the ‘perspective‘ property will adjust the perspective of the scene, similar to a focal length. Using ‘perspective-origin‘, you can control where the vanishing point is within the scene, it starts close to the center and is adjusted with the mouse movement.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.GameWorld {
    display:block;
    position:absolute;
   
    top:60%;
    left:50%;
   
    margin:0;
   
    transform-style: preserve-3d;
    transform-style: flat;
   
    -webkit-perspective: 400px;
    perspective: 400px;
   
    -webkit-perspective-origin: 0 -80px;
    perspective-origin: 0 -80px;
}

The following example shows how I’m using CSS to create an image for the Donut Shoppe. I set size of the div to be at least the width and height of the image, then set the background image to be that of the shoppe.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.DonutGetShoppe {
    width: 300px;
    height: 215px;
   
    background: url(../images/shoppe.png) no-repeat top left;
   
    transform-style: preserve-3d;
    transform-style: flat;
   
    position:absolute;
   
    -moz-transform: translate3d(-150px, -150px, -100px) rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale(1,1);
    -webkit-transform: translate3d(-150px, -150px, -100px) rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale(1,1);
   
    -webkit-transform-origin: 0 0 0;
    -moz-transform-origin: 0 0 0;
}

I painstakingly setup the positions of the walls and layers by eyeing them. I adjust the Z position to force perspective in the view, and adjust the scale to makeup for the size differences. You can use “transform-origin” to control where the pivot point of a object will be, in this case when I’m rotating the building side walls.

Make it Move!

So now that we’ve got a scene setup, here’s how we can add a little bit of motion for a little bit of effort.

In the javascript, I’m using jquery to wait for a document complete event to set a mousemove and mouseout event for the scene. On mousemove, we’ll set the ‘active’ variable to true so the perspective will be updated. As well as set the perspectivePos array to a number based on the mouse position. On mouseout, we’ll simply set ‘active’ to false.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$('.SokayAppView').mousemove(mouseMoveEvent);

function mouseMoveEvent(e) {
   
    //console.log("e: " + e + " , x:" + e.clientX + " ,y: " + e.clientY);
   
    var _xmouse = e.clientX;
    var _ymouse = e.clientY;
   
    perspectivePos[0] = _xmouse - (GAME_WIDTH/2);
    perspectivePos[1] = -80 + ((_ymouse / GAME_HEIGHT) * 80 );
   
    // enable the update on mouse out
    if (!active) active = true;
   
}

Then I take the perspective variables and update the CSS for the “GameWorld.”

1
2
3
4
5
6
7
8
function act() {
   
    if (active) {
        // update the perspective-origin css property
        $('.GameWorld').get(0).style.cssText += "perspective-origin:" + perspectivePos[0] + "px " + perspectivePos[1] + "px;" + "-webkit-perspective-origin:" + perspectivePos[0] + "px " + perspectivePos[1] + "px;";
    }
   
}

That’s it!

This might leave some questions but I’m sure you can figure the rest out from here! So have at it! ;)

There are still some issues I’m trying to work out with being able to rotate objects and create a more believable environment. There’s lot of quirks to the way I setup things here. I may give Sprite3D.js a shot to see if it solves any of these issues.

I’m still warming up to these HTML5 technologies. It’s amazing how far things have come since like Internet Explorer 3.0.

Get The APP!

DONUT GET! is a Mobile and Flash game created by the team at Sokay.net. The mobile version was developed in Unity, check out a tutorial how I create its sprite animation system.