Showing posts with label 8. Show all posts
Showing posts with label 8. Show all posts
Tuesday, March 10, 2015
What I learned about agile teams from 8 9 year old girls basketball
I am a very fortunate daddy. I am one of the coaches for my eldest daughters basketball team. The community league she plays in has a rule designed to promote team work that at first glance may seem counter intuitive - "No double teaming". This means that in most circumstances when playing defense each girl must cover her own check and can not help out her teammate. To explain this a little more, Ill give you two examples.
Read more »
Example 1 - double teaming:
Mary is new to basketball and is tentatively dribbling the ball over half court. Ann is playing defense on Mary and is doing a pretty good job of limiting where Mary can go and who she can pass to. Laura is a very strong player on Anns team and notices that Mary is struggling and that she has an opportunity to make a play. Laura leaves her own check, runs over to Mary (who is now double teamed), steals the ball and races to her own basket where she scores an easy two points. Her team cheers loudly.
Example 2 - helping:
Amber is an experienced basketball player and is dribbling the ball over half court. Kari is playing defense on Amber but is having trouble limiting where Amber can go and who she can pass to. Deanna is a very strong player on Karis team, but because she is not allowed to double team, she does not immediately race over to help Kari. Amber makes a quick move that allows her to get past Kari and race towards the basket. At this point, Kari knows she is in trouble and yells help. Deanna races over and gets in front of Amber to slow her down just enough so that Kari can catch up. Once Kari has caught up, Deanna races back to her own check and Kari resumes playing defense on Amber. No baskets are scored and mild applause is heard.
The nature of community led basketball leagues is that there is a combination of inexperienced but willing coaches and inexperienced but willing referees. This means that most times example 1 occurs without the ref or the coach interfering. While our team has been mostly executing as example 2, many of the teams we play against have been executing mostly as example 1.
Over the course of the season, the result has been interesting. Teams that were beating us at the beginning of the season by taking full advantage of the skills of their best player through double teaming are now struggling to even have a chance to score against our girls. The girls on those teams are trying just as hard, but the double teaming strategy that worked so well early in the season is no longer effective.
We have a team of 10 girls who have each improved. Some of the girls are more skilled than others, but each girl has improved noticably from the beginning of the season. The net effect is that our team has also improved noticably from the beginning of the season. Maybe more importantly, this team supports each other, learns from each other, and trusts each other.
Note: It would be easy to credit our brilliant coaching strategy, but that would clearly discount the effort, teamwork, and skill of the girls on our team. They are a wonderful group.
The parallels for agile teams are pretty clear to me. If you expect your team to perform at a high level, you need to let them improve at every role and reinforce team ownership of the project.
- If you are the PM, dont take sole responsibility of the budget, tasks, project goals etc. Make these visible to your team and help your team own them and make decisions about tasks and priorities together.
- If you are the DBA, dont get upset when the database design is insufficient, teach the team how to improve their design the first time. Work with them rather than fixing their work later.
- If you are a tester, dont assume full responsibility for testing, teach your team how to be better testers by testing every day and pair testing with the developers.
- If you are the UI expert, involve your team when designing the UI. Lead the exercise, but dont steal the design for yourself.
Of course, the converse of all of these is also true:
- If you are a developer, take responsibility for the budget and priorities
- If you are a tester, learn how to code a little
- etc.
There are other lessons to be learned from 8/9 year old girls basketball, like how to ask for help when you need it, but those are for another day.
Occasionally I meet former co-workers who inevitably ask me what Im doing. When I announce my agile and lean passions and talk even briefly about the differences between traditional teams and agile teams, I often am greeted with a response something like "But someone still has to manage the team and the budget right?" My answer is usually "Yes, but it doesnt need to be you anymore. It should be the whole team."
As the African proverb says, "If you want to go fast, go alone. If you want to go far, go together."
Saturday, January 31, 2015
Flash 8 Frame by Frame Animation and Flash 8 Onion Skin Feature Video Tutorial Series
NOTE: If you are looking for a version of this tutorial for Flash CS5, then check out Creating a Frame by Frame Animation - Flash CS5 Video Tutorials for Beginners.
In this set of Macromedia Flash tutorial online video training clips, well show you the basics of creating a simple frame by frame animation in Macromedia Flash 8. In these tutorials, youll learn how to insert blank keyframes, draw using the brush tool, and youll also learn how to use the helpful Flash 8 onion skin feature, which is a very useful tool when doing frame by frame animation in Flash 8. Well also be explaining what the FLA and SWF file formats are.
Macromedia Flash 8 lets you easily create your own frame-by-frame animation. In this video, we explain how a frame by frame animation works and we show you a simple example of a Flash 8 stickman animation.
[ALTERNATE LINK]
In part 2 of the Flash Frame-By-Frame Animation Tutorial for beginners series, we show you how to draw a simple stickman on the stage of your Flash document.
[ALTERNATE LINK]
In Part 3 of this Flash 8 video training series, well teach you how to add keyframes and how to use the Flash 8 Onion Skin feature - a feature that lets you see a tracing guide based on the objects in previous or next keyframes.
[ALTERNATE LINK]
Make it a habit to save your work often! It can save you from the frustration of losing all of your hard work.
[ALTERNATE LINK]
In Part 5 of the Flash Frame-By-Frame Animation video tutorial series, well show you how to preview your animation. This video also explains what a SWF file is.
[ALTERNATE LINK]
In the final part of the Flash Frame-By-Frame Animation Tutorial series, well show you how you can adjust the frame rate of your animation.
[ALTERNATE LINK]
Read more »
In this set of Macromedia Flash tutorial online video training clips, well show you the basics of creating a simple frame by frame animation in Macromedia Flash 8. In these tutorials, youll learn how to insert blank keyframes, draw using the brush tool, and youll also learn how to use the helpful Flash 8 onion skin feature, which is a very useful tool when doing frame by frame animation in Flash 8. Well also be explaining what the FLA and SWF file formats are.
Part 1: Flash 8 Frame-By-Frame Animation Tutorial - Introduction
Macromedia Flash 8 lets you easily create your own frame-by-frame animation. In this video, we explain how a frame by frame animation works and we show you a simple example of a Flash 8 stickman animation.
[ALTERNATE LINK]
Part 2: Flash 8 Frame-By-Frame Animation Tutorial - Drawing on the Stage
In part 2 of the Flash Frame-By-Frame Animation Tutorial for beginners series, we show you how to draw a simple stickman on the stage of your Flash document.
[ALTERNATE LINK]
Part 3: Flash 8 Frame-By-Frame Animation Tutorial - Adding Keyframes and the Onion Skin Feature
In Part 3 of this Flash 8 video training series, well teach you how to add keyframes and how to use the Flash 8 Onion Skin feature - a feature that lets you see a tracing guide based on the objects in previous or next keyframes.
[ALTERNATE LINK]
Part 4: Flash 8 Frame-By-Frame Animation Tutorial - Saving Your Document
Make it a habit to save your work often! It can save you from the frustration of losing all of your hard work.
[ALTERNATE LINK]
Part 5: Flash 8 Frame-By-Frame Animation Tutorial - Previewing Your Animation
In Part 5 of the Flash Frame-By-Frame Animation video tutorial series, well show you how to preview your animation. This video also explains what a SWF file is.
[ALTERNATE LINK]
Part 6: Flash 8 Frame-By-Frame Animation Tutorial - Adjusting the Frame Rate
In the final part of the Flash Frame-By-Frame Animation Tutorial series, well show you how you can adjust the frame rate of your animation.
[ALTERNATE LINK]
Thursday, January 29, 2015
Novo 8 Dream Tablet Firmware
Novo 8 Dream Firmware
Flashing Tool
DOWNLOAD OFFICIAL FIRMWARE
Wednesday, January 28, 2015
Creating a Pentomino game using AS3 Part 8
In this part we will add the ability to place rotated shape on the grid.
In the previous tutorials we used internal (temporary) variable inside several functions named currentValues. Lets make this a private variable available in all functions:
So in mouseMove(), instead of setting value to this variable we call updateCurrentValues():
Call the same function in mouseWheel and keyDown if there is a shape thats selected:
Go to mouseUp() and add a line that sets the new shapes rotation to cursor.rotation:
Now create a new function called clone(). This will be used to clone an array with arrays inside of it properly:
Add updateCurrentValues() function. Here in the first line we clone the respective element in shapeValues and apply that value to currentValues.
Then we loop through all the 5 elemenst of it, and go through a few if statements to modify the values. If cursors rotation is 0, we dont need to change anything, since the values in currentValues apply here. If the shape has rotation 90, we need to reverse the values of each element (x becomes y and y becomes x). If the rotation is 180 or -180, we inverse the values by multiplying them by -1. If the rotation is -90, inverse and reverse the values. If scaleX of cursor is less than 0, inverse the X values:
Now we can drag, rotate, and drop the first shape properly.
Full code so far:
Next time well implement more shapes.
Thanks for reading!
Read more »
In the previous tutorials we used internal (temporary) variable inside several functions named currentValues. Lets make this a private variable available in all functions:
private var currentValues:Array = [];
So in mouseMove(), instead of setting value to this variable we call updateCurrentValues():
private function mouseMove(evt:MouseEvent):void {
cursor.x = mouseX;
cursor.y = mouseY;
if (selectedShape > 0) {
var mousePos:Point = new Point(Math.floor((mouseX - gridStartX) / gridCellWidth), Math.floor((mouseY - gridStartY) / gridCellWidth));
canPutHere = true;
updateCurrentValues();
for (var i:int = 0; i < currentValues.length; i++) {
var cellX:int = currentValues[i][0] + mousePos.x;
var cellY:int = currentValues[i][1] + mousePos.y;
if (cellX < 0 || cellY < 0 || cellX >= mapGrid[0].length || cellY >= mapGrid.length || mapGrid[cellY][cellX]!=1) {
canPutHere = false;
}
}
}
}
Call the same function in mouseWheel and keyDown if there is a shape thats selected:
private function mouseWheel(evt:MouseEvent):void {
if (evt.delta > 0) cursor.rotation += 90;
if (evt.delta < 0) cursor.rotation -= 90;
if (selectedShape > 0) updateCurrentValues();
}
private function keyDown(evt:KeyboardEvent):void {
if (evt.keyCode == 68) cursor.rotation += 90;
if (evt.keyCode == 65) cursor.rotation -= 90;
if (evt.keyCode == 32) cursor.scaleX *= -1;
if (selectedShape > 0) updateCurrentValues();
}
Go to mouseUp() and add a line that sets the new shapes rotation to cursor.rotation:
private function mouseUp(evt:MouseEvent):void {
stopDragShape();
if (selectedShape > 0) {
if (!canPutHere) {
availableShapes[selectedShape-1]++;
shapeButtons[selectedShape-1].count.text = availableShapes[selectedShape-1];
selectedShape = 0;
}
if (canPutHere) {
var mousePos:Point = new Point(Math.floor((mouseX - gridStartX) / gridCellWidth), Math.floor((mouseY - gridStartY) / gridCellWidth));
var newShape:MovieClip = new game_shape();
addChild(newShape);
newShape.x = gridStartX + (mousePos.x+1) * gridCellWidth - gridCellWidth / 2;
newShape.y = gridStartY + (mousePos.y + 1) * gridCellWidth - gridCellWidth / 2;
newShape.rotation = cursor.rotation;
newShape.scaleX = cursor.scaleX;
newShape.scaleY = cursor.scaleY;
newShape.gotoAndStop(selectedShape);
for (var i:int = 0; i < currentValues.length; i++) {
var cellX:int = currentValues[i][0] + mousePos.x;
var cellY:int = currentValues[i][1] + mousePos.y;
mapGrid[cellY][cellX] = 2;
}
cursor.parent.setChildIndex(cursor, cursor.parent.numChildren - 1);
selectedShape = 0;
}
}
}
Now create a new function called clone(). This will be used to clone an array with arrays inside of it properly:
private function clone(source:Object):*{
var myBA:ByteArray = new ByteArray();
myBA.writeObject(source);
myBA.position = 0;
return(myBA.readObject());
}
Add updateCurrentValues() function. Here in the first line we clone the respective element in shapeValues and apply that value to currentValues.
Then we loop through all the 5 elemenst of it, and go through a few if statements to modify the values. If cursors rotation is 0, we dont need to change anything, since the values in currentValues apply here. If the shape has rotation 90, we need to reverse the values of each element (x becomes y and y becomes x). If the rotation is 180 or -180, we inverse the values by multiplying them by -1. If the rotation is -90, inverse and reverse the values. If scaleX of cursor is less than 0, inverse the X values:
private function updateCurrentValues():void {
currentValues = clone(shapeValues[selectedShape-1]);
for (var i:int = 0; i < 5; i++) {
if (cursor.rotation == 90) currentValues[i].reverse();
if (cursor.rotation == 180 || cursor.rotation == -180) {
currentValues[i][0] *= -1;
currentValues[i][1] *= -1;
}
if (cursor.rotation == -90) {
currentValues[i].reverse();
currentValues[i][0] *= -1;
currentValues[i][1] *= -1;
}
if (cursor.scaleX < 0) {
currentValues[i][0] *= -1;
}
}
}
Now we can drag, rotate, and drop the first shape properly.
Full code so far:
package
{
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.utils.ByteArray;
/**
* Open-source pentomino game engine
* @author Kirill Poletaev
*/
public class pentomino_game extends MovieClip
{
private var mapGrid:Array = [];
private var availableShapes:Array = [];
private var shapeButtons:Array = [];
private var shapeValues:Array = [];
private var placedShapes:Array = [];
private var gridShape:Sprite = new Sprite();
private var gridStartX:int;
private var gridStartY:int;
private var gridCellWidth:int;
private var cursor:MovieClip;
private var rolledShape:MovieClip;
private var selectedShape:int = 0;
private var canPutHere:Boolean = false;
private var currentValues:Array = [];
public function pentomino_game()
{
// default map
mapGrid = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 0, 0, 1, 1, 1, 1],
[1, 1, 1, 1, 0, 0, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
];
availableShapes = [20, 2, 1, 2, 1, 2, 2, 3, 4, 1, 2, 2];
shapeValues = [[[0,0],[0,1],[0,2],[0,-1],[0,-2]]];
// grid settings
calculateGrid();
addChild(gridShape);
gridShape.x = gridStartX;
gridShape.y = gridStartY;
// draw tiles
drawGrid();
// add shape buttons
for (var i:int = 0; i < 4; i++) {
for (var u:int = 0; u < 3; u++) {
var shapeButton:MovieClip = new select_shape();
shapeButton.x = 528 + u * 62;
shapeButton.y = 55 + i * 62;
addChild(shapeButton);
shapeButton.bg.alpha = 0.3;
shapeButton.count.text = String(availableShapes[3 * i + u]);
shapeButton.shape.gotoAndStop(3 * i + u + 1);
shapeButtons.push(shapeButton);
shapeButton.addEventListener(MouseEvent.ROLL_OVER, buttonOver);
shapeButton.addEventListener(MouseEvent.ROLL_OUT, buttonOut);
shapeButton.addEventListener(MouseEvent.MOUSE_DOWN, buttonDown);
}
}
// cursor
cursor = new game_shape();
addChild(cursor);
cursor.mouseEnabled = false;
cursor.mouseChildren = false;
cursor.visible = false;
addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheel);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
addEventListener(Event.ENTER_FRAME, everyFrame);
addEventListener(MouseEvent.MOUSE_UP, mouseUp);
}
private function calculateGrid():void {
var columns:int = mapGrid[0].length;
var rows:int = mapGrid.length;
// free size: 520x460
// fit in: 510x450
// calculate width of a cell:
gridCellWidth = Math.round(510 / columns);
var width:int = columns * gridCellWidth;
var height:int = rows * gridCellWidth;
// calculate side margin
gridStartX = (520 - width) / 2;
if (height < 450) {
gridStartY = (450 - height) / 2;
}
if (height >= 450) {
gridCellWidth = Math.round(450 / rows);
height = rows * gridCellWidth;
width = columns * gridCellWidth;
gridStartY = (460 - height) / 2;
gridStartX = (520 - width) / 2;
}
}
private function drawGrid():void {
gridShape.graphics.clear();
var width:int = mapGrid[0].length;
var height:int = mapGrid.length;
var i:int;
var u:int;
// draw background
for (i = 0; i < height; i++) {
for (u = 0; u < width; u++) {
if (mapGrid[i][u] == 1) drawCell(u, i, 0xffffff, 1, 0x999999);
}
}
}
private function drawCell(width:int, height:int, fill:uint, thick:Number, line:uint):void {
gridShape.graphics.beginFill(fill);
gridShape.graphics.lineStyle(thick, line);
gridShape.graphics.drawRect(width * gridCellWidth, height * gridCellWidth, gridCellWidth, gridCellWidth);
}
private function mouseMove(evt:MouseEvent):void {
cursor.x = mouseX;
cursor.y = mouseY;
if (selectedShape > 0) {
var mousePos:Point = new Point(Math.floor((mouseX - gridStartX) / gridCellWidth), Math.floor((mouseY - gridStartY) / gridCellWidth));
canPutHere = true;
updateCurrentValues();
for (var i:int = 0; i < currentValues.length; i++) {
var cellX:int = currentValues[i][0] + mousePos.x;
var cellY:int = currentValues[i][1] + mousePos.y;
if (cellX < 0 || cellY < 0 || cellX >= mapGrid[0].length || cellY >= mapGrid.length || mapGrid[cellY][cellX]!=1) {
canPutHere = false;
}
}
}
}
private function mouseWheel(evt:MouseEvent):void {
if (evt.delta > 0) cursor.rotation += 90;
if (evt.delta < 0) cursor.rotation -= 90;
if (selectedShape > 0) updateCurrentValues();
}
private function keyDown(evt:KeyboardEvent):void {
if (evt.keyCode == 68) cursor.rotation += 90;
if (evt.keyCode == 65) cursor.rotation -= 90;
if (evt.keyCode == 32) cursor.scaleX *= -1;
if (selectedShape > 0) updateCurrentValues();
}
private function updateCurrentValues():void {
currentValues = clone(shapeValues[selectedShape-1]);
for (var i:int = 0; i < 5; i++) {
if (cursor.rotation == 90) currentValues[i].reverse();
if (cursor.rotation == 180 || cursor.rotation == -180) {
currentValues[i][0] *= -1;
currentValues[i][1] *= -1;
}
if (cursor.rotation == -90) {
currentValues[i].reverse();
currentValues[i][0] *= -1;
currentValues[i][1] *= -1;
}
if (cursor.scaleX < 0) {
currentValues[i][0] *= -1;
}
}
}
private function clone(source:Object):*{
var myBA:ByteArray = new ByteArray();
myBA.writeObject(source);
myBA.position = 0;
return(myBA.readObject());
}
private function startDragShape(shapeType:int):void {
cursor.rotation = 0;
cursor.scaleX = gridCellWidth / 100;
cursor.scaleY = gridCellWidth / 100;
cursor.alpha = 0.75;
cursor.visible = true;
cursor.gotoAndStop(shapeType);
}
private function stopDragShape():void {
cursor.alpha = 0;
cursor.visible = false;
}
private function everyFrame(evt:Event):void {
if (rolledShape != null) {
rolledShape.rotation += 4;
}
}
private function buttonOver(evt:MouseEvent):void {
if(selectedShape==0){
evt.currentTarget.bg.alpha = 1;
rolledShape = evt.target.shape;
}
}
private function buttonOut(evt:MouseEvent):void {
evt.currentTarget.bg.alpha = 0.3;
rolledShape = null;
}
private function buttonDown(evt:MouseEvent):void {
selectedShape = evt.currentTarget.shape.currentFrame;
if(availableShapes[selectedShape-1]>0){
startDragShape(selectedShape);
availableShapes[selectedShape-1]--;
evt.currentTarget.count.text = availableShapes[selectedShape-1];
evt.currentTarget.bg.alpha = 0.3;
rolledShape = null;
}else {
selectedShape = 0;
}
}
private function mouseUp(evt:MouseEvent):void {
stopDragShape();
if (selectedShape > 0) {
if (!canPutHere) {
availableShapes[selectedShape-1]++;
shapeButtons[selectedShape-1].count.text = availableShapes[selectedShape-1];
selectedShape = 0;
}
if (canPutHere) {
var mousePos:Point = new Point(Math.floor((mouseX - gridStartX) / gridCellWidth), Math.floor((mouseY - gridStartY) / gridCellWidth));
var newShape:MovieClip = new game_shape();
addChild(newShape);
newShape.x = gridStartX + (mousePos.x+1) * gridCellWidth - gridCellWidth / 2;
newShape.y = gridStartY + (mousePos.y + 1) * gridCellWidth - gridCellWidth / 2;
newShape.rotation = cursor.rotation;
newShape.scaleX = cursor.scaleX;
newShape.scaleY = cursor.scaleY;
newShape.gotoAndStop(selectedShape);
for (var i:int = 0; i < currentValues.length; i++) {
var cellX:int = currentValues[i][0] + mousePos.x;
var cellY:int = currentValues[i][1] + mousePos.y;
mapGrid[cellY][cellX] = 2;
}
cursor.parent.setChildIndex(cursor, cursor.parent.numChildren - 1);
selectedShape = 0;
}
}
}
}
}
Next time well implement more shapes.
Thanks for reading!
Subscribe to:
Posts (Atom)