BLOGGER DETAILS

RECENT BLOG POSTS

HTML5 Canvas – Tap & Rotate Player with Arctangent

One of the more challenging user experiences in a game is the need to move AND aim a player on the screen.  That gets harder with mobile devices, where you have limited controller options.  One way to fix this, is to allow the user to tap where your character should aim than have him turn in that direction.  Think of a gun turret or a spaceship where all you do is tap on enemies and the turret or spaceship turns and fires in that direction.

To do this this you simply need to know what direction your object or “Actor” is currently pointing, and the location of the the object want to point at.  With a single line of Trigonometry it’s a simple thing to code.

Here’s is a quick Vine video on this approach (roll over your mouse to view)

Below are the steps to do this with code

Step 1. Figure A. : Canvas will rotate in radians.  So rather than 360 degrees in a circle think the radians as and expression of PI. Half way around a circle is 3.14 radians, and all the around is 6.28 radians, or 2PI.  So at any given point you should know your Actors Radian angle in Canvas.  In my code I simply increment (spin) the canvas by +1 or -1 each frame.  Thus it is easier for my “spin’ variable to work with 360 degrees for a smooth animation. So I calculate from radians to degrees and back to radians in my code.

 shipRadian=(spin * Math.PI/180); // this will turn 0-360 degrees into a radians from 0-6.28
 context.rotate(shipRadian);    //this will rotate the canvas to a radian

Step 2. Figure B.  To point your Actor at an enemy or a touch event on the screen, you need to calculate the radian angle of that enemy in relation to the Actor.  You can use a simple javascript math expression to do this Math.atan2(DeltaY, DeltaX).  In my example, the touch event will place a crosshair symbol on the screen, and we will then fire at that crosshair.  The code to calculate the radian we need looks like this.

 xhairRadian=Math.atan2(touchY-shipy, touchX-shipx);

Step3: Figure C & D:  The next thing to do is to subtract the new radian value by your actors radian.  That delta will give you a number either smaller or larger than PI (3.14).  Now for the best animation we want the actor in our scene to turn in a direction of the shortest path.   Generally the way to consider this is if the delta radian is smaller than PI then the rotation toward the new radian value will be clockwise, if larger than PI the rotation toward the new radian value will need to be counterclockwise

Code for Figure C & Figure D

 if(xhairRadian<=0){      // The arctangent math calulates a negative radian for half of the radians. This turns the negative radian into is positive counterpart
    xhairRadian=2*Math.PI+xhairRadian;
    }
  deltaRadian=xhairRadian-shipRadian   // Determine the detla between the ship and new radian                        
  if (deltaRadian < -Math.PI || deltaRadian > Math.PI){   // determine if the delta is beyond 3.14 or -.3.14, if so turn right i.e. clockwise
      if(xhairRadian<shipRadian){
    direction="right";
    }
    if(xhairRadian>shipRadian){
      direction="left";
      }
   }

  else {       // else if the difference in angle is positive spin toward the right
      if (xhairRadian > shipRadian) {
          direction = "right";
    }

    if(xhairRadian<shipRadian){ // if the difference in angls is negative spin toward the left
      direction="left";
      }
   }
  shotstart=1;      // shotstart = 1 means we've finished the calculations and are ready spin and shoot
     }
  }

Step4: Figure E: The next thing to do, is to start incrementing the canvas rotation in the proper direction.  Through some testing I found a static rate of movement creates a problem.  Either the ship takes too long to go around, and the action isn’t good.  Or the ship moves too quickly in short distances, and it looks choppy. To fix this, I add an accelerated speed to the rotation, where each frame I increase the speed until it his a max speed.  That creates fast and smooth action.

var speedmax =20; // our top rate of speed;
if (shotstart==1){ //if the shot was made start to spin the ship
  if (direction=="left"){ 
      spinspeed--; //if not at top speed then increase the speed of the ship turning in the negative direction
    if (spinspeed<(speedmax*-1)){  
        spinspeed = (speedmax * -1); //if you hit top speed don't increase the speed anymore
    }
  }
  else {
      spinspeed++; //if not at top speed then increase the speed of the ship turning in the positive direction
      if (spinspeed > speedmax) { //if you hit top speed don't increase the speed anymore
          spinspeed = speedmax;
      }
    spin+=spinspeed;  // our spin number increases by the rate of spin
  spinspeed *= 1.6; // increase the spin rate by 60% each frame
}

Step 5 Figure F:  Because our randians and degrees go from 0 to 6.28 and 0 to 360, when you rotate counter clockwise and pass Zero you need to change the math.  Since our”spin” variable is in degrees when we pass Zero we need to shift plus 360 rather than going negative.  Also if going clockwise after you pass 360 you need to go to Zero rather than counting up pass 360.   To manage this you’ll need a piece of code that will either subtract or add 360 to the current spin, depending on the direction.

  if (spin >= 360) {  //if you've come all the way around, reset the spin by 360
      spin = spin - 360;
  }
  if (spin <= 0) { //if you've come all the way around, reset the spin by 360
      spin=spin+360;
    }

Step 6. Figure G.  Ultimately when we get the Actor radian to match the new radian we want to stop spinning.  However when dealing with math of fractional numbers it is hard to get a number to exactly equal another number.  To make this easier all we do is add a buffer amount aound our target radian value.  Thus if our Actor is  close enough to pointing in the right direction we can go ahead and make it equal the target radian.

 if (spinRound >=xhairRadianround-0.5 && spinRound <= xhairRadianround +0.5 || spinRound >Math.PI*2  || spinRound <0) {;
//if the ships close enough to the proper angle no need to animate just point the ship at the cursor
  shipRadian=xhairRadian;
  spinspeed = spindefault;
  shotstart=0; 
    }
    else
  { //if the angle is far enough off start to spin the ship 
   shipRadian=(spin * Math.PI/180)
  }

Step 7. Figure H.  When we’ve done this we’ve completed the task of rotating our Actor in the correct direction.  With this done you can trigger the event, which in our case is firing a laser.

 if (shipRadian==xhairRadian){ // we are pointed at the place we tapped, now fire the lasers
      drawShot(); 
      shotprogress=true;  // flag to say we completed the drawing our lasers
     }

That’s all there is to this.  Check it out yourself on my public drop box: Launch Example  Open this with any HTML5 compatible device and tap around. View and copy the source to play with your own version.

Follow Bob Duffy on Twitter @bobduffy

Read more >

Use HTML5 device orientation to run Windows 8 javascript apps on Android devices

For the last year there’s been a good deal of information published on how to use the sensor APIs for Windows 8 device.  However if you are interesting in having portable HTML5 code leveraging device sensors typically required a native approach or 3rd party proprietary solution.  With a bit of work I found you can leverage new device orientation  event listeners in javascript that will mostly duplicate the Windows 8 native device sensor APIs .  And because HTML5 allows you to swap code on the fly you can easily leverage both the native sensor APIs for Windows and the html5 device orientation APIs depending on the device that is running the code. In other words, the same code you have in Visual Studio can be hosted on the web and work for an Android phone or tablet. Cool stuff!

Benefit of Coding in Javascript
Javascript is fastly becoming a highly portable language that can be used to call cross platform web browser instructions or native APIs for a particular OS.  Windows 8 allows you to compile a native app with javascript.  A neat possibility of this is the exact same code can be hosted and run by mobile devices or legacy PCs in a browser.  A problem however is very apparent when you want to leverage device specific APIs for sensors like the gyrometer , and accelerometer , etc.  While these Windows 8 classes are awesomely powerful to access via javascript, only Windows can execute them.  Thankfully the HTML5 events are catching up and can allow you to capture data from the device to get the device rotation information across all 3 axis. 

With just a bit of work you can tweak the data to mimic the native sensor APIs, and plug into your core code, creating a seamless experience across devices and form factors. Note you may be able to do this using PhoneGap and other 3rd party solutions, however that is brokering the solution to another entity. That might be good, that might not.  I’m certain that point is up for debate

Device Orientation Browser Compatibility

This is a fairly new event listener, but it is pretty well adopted and can be used on Chrome, Firefox and Opera and their mobile counterparts. Note IE10 does not support, however as I show you can swap out the Windows 8 sensor events with HTML5 sensor events and visa versa. Check out the graph of support from http://caniuse.com/deviceorientation.

Sample Code – Sensor Event Listener in Windows 8 vs HTML5

In the below code I’m showing how I get data from the Windows 8 Sensor API to turn move and rotate an object in my game.  With my example app I use the variable “webapp” to determine which code to execute.
Note in this use case I’m reading sensors and assigning data to 3 variables: xAngleyAngle and zAngle. xAngle is usedto alter the X position of my object on Canvas.  yAngle alters the Y Position of an object in Canvas, and zAngle rotates my object, like a spinning top, either left of right.

if (webapp == false) { // use Windows 8 Sensor API
    gyrometer = Windows.Devices.Sensors.Gyrometer.getDefault();
    gyrometer.addEventListener("readingchanged", onGyroReadingChanged);
    accelerometer = Windows.Devices.Sensors.Accelerometer.getDefault();
    accelerometer.addEventListener("readingchanged", onAccReadingChanged);
}
 
function onGyroReadingChanged(e) { // gets data for rotation around Z Axis and assigns to zAngle
    var accelZ = e.reading.angularVelocityZ;
    zAngle = -accelZ.toFixed(2);
}
 
function onAccReadingChanged(e) { //gets the tilt information and assigns to xAngle and yAngle
    var inclX = e.reading.accelerationY.toFixed(2) * -90;
    var inclY = e.reading.accelerationX.toFixed(2) * 90;
    xAngle = inclY;
    yAngle = holdAngle + inclX; // hold angle is read or set in calibration function
}
 

Here is the  base HTML5 device orientation version of that code.  Note this isn’t done, read further to understand how I have to adjust this.

 

if (webapp == true) { // use HTML5 device orientation event listener
    window.addEventListener('deviceorientation', capture_orientation, false);
}
 
function capture_orientation(event) { //set input for web browser orientation sensors
    var alpha = event.alpha;
    var beta = event.beta;
    var gamma = event.gamma;
    alphaAngle = alpha.toFixed(2)  //set the alpha number to an integer
    xAngle = gamma;
    yAngle = holdAngle + beta;;
    zAngle = -alphadelta*5 ;
}

Solving issues between Windows 8 Sensor API and HTML5 device orientation

Defining a normal Z-Axis orientation: One issues you may have is with the Z axis rotation (gyrometer vs alpha).  Unless your use case is a compass, you will find that there is no “normal” orientation for the Z axis.  For example the X & Y axis rotation, you can assume that the X & Y plane are at a normal or default position if parallel to the plane of the earth (flat on a table). Thus if your device is tilted on its side or pitched forward your app might rotate something. However a user can be holding the device anywhere on across the Z axis and assume that experience should give them a normal or default experience.  In other words if you are facing North instead of East when you start your app, for most applications you assume it’s going to be the same experience.  Thus a key to having the Z axis orientation work in your app is to turn that axis orientation into accelerated data.  That way you know the difference or speed the device is moving around a Z axis.  In other words a still device that is not spinning, is your “normal” or default orientation for the Z axis, and the data you want is how fast and in what direction are you rotating on the Z axis.  .

The device orientation event handler, however, does not provide that accelerated data directly.  You will have to interpret the difference in the change of the data to get something like the accelerated Z axis spin.  However once you do this, the data is very comparable to the gyrometer data you get from the Windows 8 native sensor API.  To fix this I determine the difference between the previous or last Alpha orientation and the current Alpha orientation. That gives me a number that almost exactly matches the gyrometer acceleration data I get from my native code.  Here’s an example this would replace the last line of our capture_orientation function

if (alphainit < 1) { //we don’t have a lastAlpha reading so we need it to equal alpha the very 1st time
        lastAlpha = alphaAngle;
        alphainit = 1; //  now have the first alpha so this code won’t run again
    }
   
    alphadelta = alphaAngle - lastAlpha; //determine the delta difference current and last alpha
    lastAlpha = alphaAngle;  //sets lastAlpha value
    zAngle = -alphadelta * 5 // this is the same as before
}
 

Swapped X & Y Axis: Another issue is that you’ll find for phones the X & Y axis (beta and gamma) data is swapped compared to tablet and PC devices.  Perhaps the default or “normal” orientation on a phone is considered portrait, and thus why beta and gamma are reversed.  For you it means that you will have to swap the gamma and beta data if you want the experience to be consistent in landscape mode across form factors. 

To manage this situation I created a variable called “mobile” and when “mobile==true” we swap of the beta and gamma data. The following code replaced our” var beta =” and “var gamma =”  lines in our capture_orientation function.

if (mobile == true) { //swap beta and gamma for mobile browsers
        var beta = event.gamma*-1;
        var gamma = event.beta;
    }
    else {
        var beta = event.beta;
        var gamma = event.gamma;
    }
    

 

Managing browser nuances: As with any web application you will have to get some information on the device, on its browser and adjust some variables. The more devices you can test your app against the more bullet proof you can make the experience.  The good thing is you only need to edit a small set of code to manage what code should be turned on or off depending on the device and browser.  Here is an example of my config.js which does this.  With it I determine information about the device and browser then I can set variables to be true or false which tailor the code to that device.  For example if the device is not a PC or tablet I set the variable mobile to be true. If it is not running MSIE (Microsoft Internet Explorer) then this is being run in a browser and set the webapp variable to be true

 

var str2 = navigator.platform;
var str3 = navigator.userAgent;
 
if (str3.indexOf("MSIE") >= 0) { //IE browser based so Windows 8 APIs
    var webapp = false;
    var tabletmode = false;
    var mobile = false;
 
}
 
else { //run as a webapp and use device orientation
    var webapp = true;
    var tabletmode = true;
 }
 
 if (str2.indexOf("Win") >= 0 || str.indexOf("Opera") >= 0 || str2.indexOf("686") >= 0) {
     //If Windows, or Opera we will not reverse the X & Y
     var mobile = false;
 }
 
 else { // this is likely a phone and we need to reverse the X & Y
     var mobile = true;
     var tabletmode = true;
 
 }
 

Try it out. Check out my test game via my pubic dropbox link. http://db.tt/4ch0jZJ4.  If you have a new PC with orientation sensors try this in Chrome, then also try on an Android tablet or phone.  Take note, if running on Android. This is the exact same code I used to compile for Windows 8 running in your browser.  Now if you have issues let me know. The more browsers and devices test the more I can optimizes the code to accommodate, which is a benefit to javascript and HTML5.

– you can contact Bob Duffy on twitter @bobduffy

 

Read more >

Did Ars Technica forget the developer ecosystem in its Win 8 article

Earlier this week I was forwarded and article fromArs Technica by Peter Bright questioning if Windows 8 is really cut out for tablet. Overall it’s a fair article with a bit of tea reading, where Peter questions if Microsoft is really fully committed to tablet with Windows 8. In reading I kept thinking Ars Technica is reading things a bit backwards, and may not be considering the developer ecosystem. Read more >

Ultrabook Work, Create, Play Challenge – 15mm of Game Dev Goodness

Ultrabook Game DevAt CES2012 will.i.am expressed that when he grew up, music was made in a music studio then played on records, but how cool it is that today music is created on computers and played on computers. I’ve been thinking about this idea. Can the Ultrabook be versatile for other work/create/play scenarios. So why not game development? How well can you both create and play games on an Ultrabook?

Here’s the task I’ve challenge the Ultrabook with
– Create concept art
– Model game objects from concept art
– Code sample game
– Play sample game

Read more >