SCOMaster – Gameplay

I was finally able to record a round of my SCOMaster game! Recap – It’s a multiplayer WiiFlash game designed to be played with teams in an instructor lead classroom setting. The object is to assemble a learning program structure the fastest – but watch out, because your opponent can take one of your SCOs and reuse them in their own program. Developed in Flash CS3 in about 150 hours.

SCOMaster Gameplay

WiiFlash Graffiti Gestures

I’ve been playing around with using gestures with WiiFlash as an alternative input scheme. As a first step, I’ve taken the Flash Gesture recognition code from Didier Brun and modified it to work with a Wii remote instead of the mouse. It works pretty well – some of the letters aren’t easy to hit – but not bad for a first try. I’d like to modify with further have more “shapes” rather than “letters” to match. More to come on this when I get extra time.

SCOMaster – WiiFlash mulitplayer learning game

SCOMaster screen shot

After a week and a half I finally finished my first real WiiFlash game – SCOMaster. It’s a competative, 2 player learning game designed to be played in a group setting. It illustrates the concepts of assembling a course in an LMS system. Bonus points are awarded for content reuse – they are SCOs after all! Unneeded objects and faulty objects can be sent back to the development team to be reworked – but at a cost.First one to build a completed activity tree wins.

Ben Hutchens, our graphic designer, created the art work for it. It took about 60 hours to program it using the WiiFlash classes that I had developed earlier in the year.

WiiFlash Tip #4 – Revisiting Wii-mouse in a multiplayer scenario

In February, I posted about using the Wii remote as a virtual mouse in Flash. While that approach seems to work just fine in a simple single player application, I found that it breaks really badly when you another player to it. I just completed my first two player WiiFlash game and I’ll document a few things that I had to differently.

Originally,  based all of the interaction with the cursors off of the typical mouse events (rollover, rollout, etc.), but this did allow for find out which player rollover the sprite or which player “clicked” on the sprite.

Each player’s cursor is just a sprite itself, so I switched to using the hitTestObject function against each players cursor sprite and the object sprite on a mouse event. This returns which of the cursors interacted with the object. Additionally, since the cursor sprites are big, you do have to see them from a distance, I found that I needed to add an additional sprite to the cursor – the cursor “point” – at 0,0 and use that for the hitTest rather than the actual big cursor sprite. If the “tail” of the cursor arrow is still over the object sprite, you shouldn’t count that as a roll over, since the point of the arrow is the important part.

So here are the functions for a rollover:

 private function onItemOver(e:MouseEvent):void {
var wm:Array = whichWiiMotesAreOverMe(Sprite(e.target))
for (var i:int = 0; i < wm.length; i++) {
_WiiMotes[wm[i]].doRumbleSeconds(.1);
_WiiMotes[wm[i]].cursorState = WiiCursorView.CURSOR_POINT;
}
}

// returns array of which wiimote cursors are over the sprite
private function whichWiiMotesAreOverMe(tgt:Sprite):Array {
var a:Array = new Array();
// _WiiMotes is an array of Wii controller objects
for (var i:int; i < _WiiMotes.length; i++) {
if (tgt.hitTestObject(_WiiMotes[i].cursorPoint)) {
a.push(i);
}
}
return a;
}

The functions for a mousedown are:

private function onItemDown(e:MouseEvent):void {
// gets the index of the _WiiMote object that clicked the sprite
var wm:int = whichWiiMoteClickedMe(Sprite(e.target));
}

// returns index of which wii mote cursor is over with the A button down
private function whichWiiMoteClickedMe(tgt:Sprite):int {
var wm:Array = whichWiiMotesAreOverMe(tgt);
for (var i:int = 0; i < wm.length; i++) {
if (tgt.hitTestObject(_WiiMotes[wm[i]].cursorPoint)) {
if (wm.length == 1) {
// simple test if only one cursor is over
if(_WiiMotes[wm[i]].isADown) return wm[i];
} else {
// little harder if 2+ are over, _LastClickWMIdx is the last Wii mote to have pressed a button
if(_WiiMotes[wm[i]].isADown && _LastClickWMIdx==wm[i]) return wm[i];
}
}
}
return -1;
}

This method, while probably not the best way, turned out to work really well in the game.

Wii’re done

The BAC Wii project has come to an end for now – we delivered our final report yesterday. Hopefully word of what we’ve done will spread and we’ll be ask to share it with more groups. It was a really cool project to be on – the potential of the Wii and kinesthetic learning is an interesting area – and I don’t think that many people are doing much with it yet. Replacing the mouse with a Wii remote is a great way to increase engagement – I wonder if I could find a way to apply to compliance training? Would make for an interesting course assessment.

Anyway, I’m posting my outline of the Wii controller class that I spent all of January developing. It makes programming for WiiFlash super simple – just instantiate it and boom – Wii support in any Flash app. Works with all typical mouse events.

Wii Controller Engine

  • Is an interface between Wii controllers (Remote and Nunchuk) WiiFlash and the application or game
  • Enables quick ramp up for a developer without having to learn the intricacies of the Wii controllers or their data
    • Can add simple Wii controller to an app in as little as 3 lines of code
    • Contains about 1000 lines of code (not including virtual mouse or WiiFlash itself)
    • WiiFlash returns raw data – engine normalizes it up to make it useful for a developer
    • The Wii controllers return small, but long numbers – 0.000000007199879823173
    • “Smoothing” out the data – eliminates the hand jitter by throwing out small changes and/or averaging data over time
    • Accesses the cursor’s X and Y position – WiiFlash gives you the location of the IR points (prefers 2 of them) as the Remote sees them on a scale from 0-1 – not in relation to your screen area.  Engine handles switching from 2 IR points visible to 1 IR point and convert the data to exact X and Y coordinates of where you’re pointing
  • Creates a secondary “virtual mouse” that allows the Wii mote to act as your normal mouse (separate from WiiFlash’s built-in “mouse control” mode) within the boundaries of your Flash movie
    • Uses Senocluar’s VirtualMouse class
    • A button is single click, B is double click
    • Makes it simple to add Wii capabilities to your app since it works with the mouse controls that you’re used to developing for
    • Allows for custom mouse cursors and the ability to switch between them with 1 line of code
    • Mouse cursors are contained in an external SWF
  • Creates a standard way to getbutton press and release state information for the Remote and the Nunchuk.Provides “isDown” data for major buttons on the Remote and Nunchuk: A, B,C and Z
  • Controls rumble – set the duration
  • Provides Nunchuk data in the same way as the Remote: rotation, acceleration, etc.
  • Nunchuk thumb stick – returns the position of the nub from -100 to +100
    • Also can use the 8 corners as buttons, names represent a compass: N, E, S and W
  • Created (but not tested) with multiple Wii motes in mind – should easily scale to up to 4 Wii motes

WiiFlash 0.4 released!

WiiFlash 0.4 has been released today! New features include: 4 IR points on the sensor bar, tracking the size of the IR points (for determining depth), and battery information from the Wii remote.

Since Jason has been busy trying to get a good depth (z-axis) reading with the 0.3.2 release – this is very welcome news. I’d wondered how Nintendo managed to get this information and it never occured to me that the size of the IR point would be it – but it makes perfect sense.

Sadly our Wii research project ends tomorrow – we give our final presentation to the sponsors at 1pm. I hope that we get to keep the Wii motes and sensor bars – there are a few minigames that I’d like to make. It would be cool to make some WiiFlash games that Sophie could play.

WiiFlash Tip #2 – Smoothing out the edges, part 2

.crux. added a comment about a different way to smooth out the data from the Wii controller – create an array of the last 10 values and average them. I’ve modified my code to try out this idea, and it’s not too bad. The data does wobble a little, but it’s more precise than my method. If you increase the size of the array, it gets smoother, but will also increase the cpu power required. Partial code below.

 private var WiiRollDegAry:Array = new Array();

public function get roll():int {
return averageIntArray(WiiRollDegAry);
}

private function updateData(pEvt:WiimoteEvent):void {
WiiRollDegAry = addValueToLimitedArry(WiiRollDegAry, int(TheWiimote.roll * TO_DEG));
}

private function addValueToLimitedArry(a:Array, v:int):Array{
a.push(v);
// increase to smooth out data
if (a.length > 10) a.shift();
return a;
}

private function averageIntArray(a:Array):int {
var len:int = a.length;
var c:int = 0;
for (var i:int = 0; i < len; i++) {
c += int(a[i]);
}
return int(c / len);
}

WiiFlash Tip #3 – Blindly pointing

Not so much a tip but a point in the right direction. These tips are really out of order!

Once you have WiiFlash working and you have your senor bar, you need be able to read X and Y values based on where you’re pointing the controller. I tried a few approaches for this, but none worked better than the code that Andrés Santos developed. Check out his wiimoteIR class and demo code. I’m using a very slighly modified version of it in my classes and it’s working great.

WiiFlash Tip #2 – Smoothing out the edges

This is tip #2 in my series on WiiFlash.

Once you have data coming in the from a Wii controller, the first thing you’ll notice is how precise it is. I don’t have it in front of me, but the precision goes about 15 places past the decimal (4.042584267432343…). If you just convert the data from radians to degrees, WiimoteObj.roll*(180 / Math.PI), and apply it to a sprite with an enter frame event, you’ll see it wobble all like mad.

There are two things that you must smooth out to best use the Wii controller – the roll, pitch and yaw of the controller, and the animation of the Wii mouse pointer.

1 – Smoothing Roll, Pitch and Yaw Data

I’m only going to demonstrate for the roll value – it’s the same for pitch and yaw. There are probably numerous ways to optimize this code, but it’s working me right now. My method involves comparing the current value to the last value and checking to see if it’s outside of a tolerance value. If so, then the value is updated, if not, then it’s ignored. I’ve found that 10 degrees works well for me, but you can change it depending on how precise you need it to be. This code assumes that you have an instance of the Wiimote class, WiimoteObj, and have the listeners set up to call the updateWiiMoteData function when the WiimoteEvent.UPDATE event occurs.

private var WiiMPitchDeg:int;
private var WiiMPitchDegPrev:int;
// to convert radians to degrees
private static const TO_DEG:Number = 180 / Math.PI;
// smoothes out roll, pitch and yaw values, smaller = more precision
private static const WIGGLE_TOL:int = 10;

private function updateWiiMoteData(pEvt:WiimoteEvent):void {
WiiMRollDeg = int(WiimoteObj.roll*TO_DEG);
var rDelta:Number = Math.abs(Math.abs(WiiMRollDegPrev) – Math.abs(WiiMRollDeg));
if(rDelta < WIGGLE_TOL) WiiMRollDeg = WiiMRollDegPrev;
}

As you can see it’s pretty simple and works well.

2 – Smoothing out the Wii mouse cursor

This one is very easy. All that you need to do is to slightly tween your cursor sprite from it’s current location to the location that the Wii more is pointing to. Here’s my function, same assumptions as above. wiiPoint is a Point object containing the location you calculated for where the controller is pointing to. I’m using Tweener for the animation.

 private function updateCursor():void {
var newx:int = 0;
var newy:int = 0;
var newr:int = 0;
try {
newx = wiiPoint.x;
newy = wiiPoint.y;
newr = WiimoteObj.roll;
} catch (e:*) {
// no good data – the ‘mote hasn’t seen the IR points
newx = 0;
newy = 0;
newr = 0;
}
Tweener.addTween(CursorSprite, {x:newx, y:newy, rotation:newr, time:.1, transition:”easeOutQuad” } );
}

For your VirtualMouse object, be sure to set it’s x and y properties to the location of the cursor sprite, not where your code says the Wii is pointing at.