Creating a Simple Game in JavaFX (Part 6)

October 27th, 2009

So far we have a working game but we are still missing an essential ingredient: score keeping.

Displaying the Score


...
var scoreTime = 0;
var scoreMoves = 0;
var scoreBonus = 0;
var scoreTotal = 0;
var scoreHigh = 0;

var scoreArea: Group = Group {

    content : [
        Rectangle {
            arcWidth: 20  arcHeight: 20
            x: 290 y: 0
            width: 120, height: 200
            fill: Color.LIGHTGOLDENRODYELLOW
            stroke: Color.DARKORANGE
            strokeWidth: 3
        },
        Text {
            font : Font {
                size: 24
            }
            layoutX: 320 layoutY: 30
            content: "Score"
        },
        Tile {
            columns: 2
            rows: 5
            hgap: 5
            vgap: 5
            tileWidth: 50
            hpos: HPos.LEADING
            layoutX: 290 layoutY: 50
            content: [
                Text {
                    font : Font {
                        size: 12
                    }
                    content: "Time:"
                },
                Text {
                    font : Font {
                        size: 12
                    }
                    content: bind scoreTime.toString()
                },
                Text {
                    font : Font {
                        size: 12
                    }
                    content: "Moves:"
                },
                Text {
                    font : Font {
                        size: 12
                    }
                    content: bind scoreMoves.toString()
                },
                Text {
                    font : Font {
                        size: 12
                    }
                    content: "Bonus:"
                },
                Text {
                    font : Font {
                        size: 12
                    }
                    content: bind scoreBonus.toString()
                },
                Text {
                    font : Font {
                        size: 12
                    }
                    content: "Total:"
                },
                Text {
                    font : Font {
                        size: 12
                    }
                    content: bind scoreTotal.toString()
                },
                Text {
                    font : Font {
                        size: 12
                    }
                    content: "High:"
                },
                Text {
                    font : Font {
                        size: 12
                    }
                    content: bind scoreHigh.toString()
                }
            ]
        }
    ]
}

Stage {
    ...
    scene: Scene {
        ...
        content: Group {
            ...
            content: [
                playingField,
                player,
                enemies,
                gameOverScreen,
                scoreArea
            ]
        }
    }
}

At this point you should be comfortable with the new code above. Nonethless there are two new aspects that we have not covered earlier. I will discuss them as we go through the code.
At the top I have added different variables for the different types of score keeping. ScoreTime keeps the score collected for running of the game. With each second passed the player automatically receives points. ScoreMoves collects the score when the player is moving. When the player is not moving, points will be deducted from scoreMoves. ScoreBonus is the score received when a player touches an enemy in bonus mode. Not touching this enemy will result in points being deducted from the scoreBonus score. Finally the total score adds up all the scores mentioned above and the high score persists the highest achieved score for the duration of the applications life. This means if you close the game, the high score is lost. This behaviour is of course not desirable, and would need fixing in future versions of the game.

The scoreArea is a familiar Group object that encompasses a Rectangle which is the background of the score area, a title object and then a new type of object called a Tile. A tile is one of several layout objects added to JavaFX in version 1.2 and is basically a grid in our case of two columns and 5 rows. All tiles or cells are of the same size. The objects are added in the content[] sequence, with the sequence position determining which position in the grid the object will have.

All we are adding to the Tile object are Text objects. The first Text object of each row is the label and the second Text object is the corresponding score. Now for the second new aspect. The value of the score is defined as bind scoreTotal.toString(). What this does is bind the value of the Text object to the value of the scoreTotal variable (as String (.toString())). This is an incredibly simple way of having your score Text object updated each time the score changes. No need for extra code, extra listeners etc. etc.

Don’t forget to add the new scoreArea to the main Group.

Our game screen now looks like this:

img 5

Adding Time Score

Now that we have the graphics in place lets calculate the time score.


                ...
                // checkCollision
                for (enemy in enemies) {
                    if (checkCircleCollision(player.translateX,player.translateY,player.radius,enemy.translateX,enemy.translateY,10.0)) {
                        gameOver();
                    } else {
                        calculateNewScore();
                    }

                }
            }
        }
    ]
}

function calculateNewScore() {
    scoreTime += 5;
    scoreTotal = scoreTime + scoreMoves + scoreBonus;

}

As always it is in our game play Timeline object that score calculations take place. After we have checked that no collision with enemy players has taken place we call the method calculateNewScore(). Notice that we do not only update the scoreTime variable with 5 points every cycle but that we are already calculating the total score which is made up of scoreTime, scoreMoves, scoreBonus.
Also notice that just as we have pointed out before, through the binding mechanism, once we update the scoreTime and totalScore variables, the corresponding text objects are also updated.

There is still a little bug in the above code. Each time you reach game over, the score is not reset. Lets make the necessary changes to start each game with a fresh new score:


function initGame():Void {
    initActors();
    initScore();
    mode = MODE_RUNNING;
    gameOverScreen.visible = false
}

function initScore() {
    scoreBonus = scoreMoves = scoreTime = scoreTotal = 0
}

All it took was a small change in initGame() and a new function called initScore().

Adding Move Score


var timeline: Timeline = Timeline {
    ...
    keyFrames : [
        KeyFrame {
            ...
            action: function() {
                var playerMoved = false;
                ...
                // Player
                if (moveRight) {
                    var newX = player.translateX + PLAYER_BALL_STEP;
                    if (newX < playingField.boundsInLocal.maxX - player.radius - playingField.strokeWidth) {
                        playerMoved = true;
                        player.translateX = newX
                    }
                }
                if (moveLeft) {
                    var newX = player.translateX - PLAYER_BALL_STEP;
                    if (newX > playingField.boundsInLocal.minX + player.radius + playingField.strokeWidth) {
                        playerMoved = true;
                        player.translateX = newX
                    }
                }
                if (moveUp) {
                    var newY= player.translateY - PLAYER_BALL_STEP;
                    if (newY > playingField.boundsInLocal.minY + player.radius + playingField.strokeWidth) {
                        playerMoved = true;
                        player.translateY = newY
                    }
                }
                if (moveDown) {
                    var newY= player.translateY + PLAYER_BALL_STEP;
                    if (newY < playingField.boundsInLocal.maxY - player.radius - playingField.strokeWidth) {
                        playerMoved = true;
                        player.translateY = newY
                    }
                }
                // checkCollision
                for (enemy in enemies) {
                    if (checkCircleCollision(player.translateX,player.translateY,player.radius,enemy.translateX,enemy.translateY,10.0)) {
                        gameOver();
                    } else {
                        calculateNewScore(playerMoved);
                    }

                }
            }
        }
    ]
}
...
function calculateNewScore(addMoveScore:Boolean) {
    scoreTime += 5;
    scoreMoves = if (addMoveScore) scoreMoves+5 else scoreMoves-10;
    scoreTotal = scoreTime + scoreMoves + scoreBonus;

}

Calculating a move score only requires very subtle changes. In the main loop we introduce a variable called playerMoved of type Boolean. If a new position is calculated for our player object, then playerMoved is set to true.
We have also updated the function calculateNewScore to accept a parameter indicating whether the player has moved or not. If the player has moved we add 5 points to the move score otherwise we subtract 10 points.

Adding Bonus Score

Adding the bonus score is a bit trickier. Here are the code changes in Main.fx:


var bonusTimeline: Timeline = Timeline {
    repeatCount: 1
    keyFrames : [
        KeyFrame {
            time : 3s
            canSkip: false
        }
    ]
}
...
var timeline: Timeline = Timeline {
    ...
    keyFrames : [
        KeyFrame {
            ...
            action: function() {
                var playerMoved = false;

                calculateBonusEnemy();
                for (enemy in enemies) {
                    if (checkCircleCollision(player.translateX,player.translateY,player.radius,enemy.translateX,enemy.translateY,10.0)) {
                        if (not enemy.bonusMode) {
                            gameOver();
                        } else {
                            calculateNewScore(playerMoved,true);
                        }
                    } else {
                        calculateNewScore(playerMoved);
                    }
                }
            }
        }
    ]
}
function calculateNewScore(addMoveScore:Boolean,addBonusScore:Boolean) {
    scoreBonus += 1000;
    calculateNewScore(addMoveScore);
}
function calculateBonusEnemy() {
    var bEnemy:Enemy = null;
    for (enemy in enemies) {
        if (enemy.bonusMode) {
            bEnemy = enemy;
        }
    }

    // None Bonus
    if (bEnemy==null) {
        if (javafx.util.Math.random()>0.95) {
            // Change one to bonus
            var i = (javafx.util.Math.ceil( enemies.size()*javafx.util.Math.random() )).intValue();
            println("changing to bonus {i}");
            enemies[i].fill(Color.CYAN);
            enemies[i].bonusMode = true;
            bonusTimeline.playFromStart();
        }
    } else {
        // One Bonus
        if (not bonusTimeline.running) { // min 3s in Bonus
            if (javafx.util.Math.random()>0.5) { // Turn off bonus
                bEnemy.fill(Enemy.DEFAULT_FILL);
                bEnemy.bonusMode = false;
                if (not bEnemy.bonusWasHit) {
                    scoreBonus -= 5000
                }
                bEnemy.bonusWasHit = false
            } else {
                // prolong bonus
                bonusTimeline.playFromStart();
            }

       }
    }
}
...

And the code changes in Enemy.fx:


...
public var bonusMode = false;
public var bonusWasHit = false; // Player hit in Bonus Mode
...
public function fill(color:Color) {
    enemy.fill = color;
}

In Enemy.fx all we do is make our Enemy objects aware that they could be in bonus mode (bonusMode) and whether they were hit in bonus mode (bonusWasHit). Furthermore we add fill function that allows us to change the fill color of the enemy objects.

The bigger changes are in Main.fx. In each cycle we now have a function calculateBonusEnemy() being called. This method first checks if an enemy is in bonus mode. If no enemy is in bonus mode it randomly decides to change one enemy to bonus mode (if random number > 0.95). Which enemy is set to bonus mode is also randomly determined. The bonus enemy gets its color changed and the bonusMode variable set to true. Furthermore a bonusTimeline is played which serves the sole purpose of running for three seconds.

If no enemy is in bonus mode (not bonusTimeline.running) there is a 50/50 chance that the bonus mode will be turned off or that the bonus mode will be prolonged. When turning of the bonus mode we also check if the enemy was hit during its bonus time and if not, 5000 points are subtracted from the bonus score. It pays to hit enemies in bonus mode.

The other changes in Main.fx are quite simple. If a player collides with a bonus enemy, 1000 points are added to the players score for each cycle.

Adding High Score


// Function called after collision
function gameOver():Void {
    timeline.stop();
    mode = MODE_GAME_OVER;
    gameOverScreen.visible = true;
    if (scoreTotal > scoreHigh)
        scoreHigh = scoreTotal;
}

Adding the High Score functionality is as simple as checking if the current total score when the gameOver() function is called is higher than the previous High Score.

We're done! We now have the final version of our game. Please check part 7 for some final thoughts, a functioning version of our game and the complete tutorial code.

Creating a Simple Game in JavaFX (Part 5)

October 22nd, 2009


We now have a player object we can control and we have enemy objects moving around the playing field. What we are still missing is code to deal with enemy player collisions and score keeping. This article will deal with calculating collisions.

Calculating Collisions

Once again it is in the Timeline responsible for managing our play cycle where we need to check for collisions.


var timeline: Timeline = Timeline {
    ...
    keyFrames : [
        KeyFrame {
            ...
            action: function() {
                // move enemy and player to new positions
                ...
                // checkCollision
                for (enemy in enemies) {
                    if (checkCircleCollision(player.translateX,player.translateY,player.radius,enemy.translateX,enemy.translateY,10.0)) {
                        gameOver();
                    }
                }
            }
        }
    ]
}

// Function called after collision
function gameOver():Void {
    timeline.stop();
}

function checkCircleCollision(c1X:Number,c1Y:Number,c1R:Number,c2X:Number,c2Y:Number,c2R:Number):Boolean {
    var distanceSquared = ((c1X - c2X) * (c1X - c2X)) +  ((c1Y - c2Y) * (c1Y - c2Y));
    var radiiSquared = (c1R + c2R) * (c1R + c2R);

    if (radiiSquared > distanceSquared) {
        return true
    }

    return false
}

After we have moved our enemies and player to new positions we need to check in each game play cycle, if they have collided. This is done in the function checkCircleCollision(…). The parameters of this function are the x and y coordinates and the radius of the player and of one enemy circle. More details about the mathematics of calculating such a collision can be found here: http://gpwiki.org/index.php/C:Collision_detection_between_two_circles.

If the player collides with any of the enemy objects the method gameOver() is called and gameplay is stopped by halting the Timeline object responsible for our game play.

Restarting the Game after Game Over

Of course we would like to let players of our game restart the game without reloading or restarting our JavaFX application. These are the necessary code changes:



def MODE_GAME_OVER: String = "GAME_OVER";
def MODE_RUNNING:   String = "RUNNING";

var mode:String = MODE_RUNNING;

var gameOverScreen: Group = Group{
    visible:false
    var r:Rectangle = Rectangle {
        arcWidth: playingField.arcWidth  arcHeight: playingField.arcHeight
        width: playingField.width, height: playingField.height
        fill: Color.ROSYBROWN
        stroke: Color.DARKRED
        strokeWidth: playingField.strokeWidth
    }
    content: [
        r,
        Text {
            font : Font {
                size: 24
            }
            x: 10, y: 50
            content: "Game Over"
        },
        Text {
            font : Font {
                size: 20
            }
            x: 10, y: 80
            content: "(press Enter to restart)"
        }
   ]
}

Stage {
    ...
    scene: Scene {
        ...
        content: Group {
            ...
            content: [
                playingField,
                player,
                enemies,
                gameOverScreen
            ]
            onKeyPressed : function (e: KeyEvent){
              if (mode==MODE_GAME_OVER) {
                if (e.code == KeyCode.VK_ENTER) {
                    initGame();
                    timeline.playFromStart();
                }
              }
           }
...
}
// Function called after collision
function gameOver():Void {
    timeline.stop();
    mode = MODE_GAME_OVER;
    gameOverScreen.visible = true;
}

function initGame():Void {
    initActors();
    mode = MODE_RUNNING;
    gameOverScreen.visible = false
}

// Function is used to initialize the actors i.e. player and enemies
// on the playing field
function initActors() {
    player.translateX = 250;
    player.translateY = 250;
    for (enemy in enemies) {
        enemy.translateX = randomEnemyInitPosition();
        enemy.translateY = randomEnemyInitPosition();
    }
}

This code is easy to understand. We introduce a variable called “mode” and define two constants MODE_GAME_OVER and MODE_RUNNING. These are the two states that are possible in our game.
We then define a Game Over screen which is a simple rectangle which is the same size as our playing field and whose visibility property is initially set to “false”. Remember that the game starts running, so we do not want to see the game over screen.
We then add the gameOverScreen object to the visible elements of our Scene. Notice how it gets added as the last object. This is necessary because once the gameOverScreen becomes visible it should appear on top of everything else.
Notice how in the gameOver() function we now set the mode to MODE_GAME_OVER.
In our KeyListener we now check if the mode of our game is MODE_GAME_OVER if so and the ENTER key has been pressed we reinitialize the game by calling the init() function. The init() function resets the player and enemy positions, changes the state or mode of our game to MODE_RUNNING and hides the game over screen. Finally our main game loop is restarted by calling timeline.playFromStart().

That concludes this part of our tutorial. In the next part we will add all of the scoring functionality. Below is a listing of the complete code so far:

Main.fx


/*
 * Main.fx
 *
 * Created on 15.10.2009, 17:36:19
 */

package myballgame;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.Group;

import javafx.scene.shape.Rectangle;

import javafx.scene.shape.Circle;

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;

import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;

import javafx.scene.text.Font;
import javafx.scene.text.Text;

/**
 * @author Alexander Gnodtke
 */
def PLAYER_BALL_STEP = 4;

def MODE_GAME_OVER: String = "GAME_OVER";
def MODE_RUNNING:   String = "RUNNING";

var mode:String = MODE_RUNNING;

var moveUp = false;
var moveDown = false;
var moveLeft = false;
var moveRight = false;

var playingField: Rectangle = Rectangle {
    arcWidth: 20  arcHeight: 20
    width: 280, height: 380
    fill: Color.ANTIQUEWHITE
    stroke: Color.DARKORANGE
    strokeWidth: 3
}

var gameOverScreen: Group = Group{
    visible:false
    var r:Rectangle = Rectangle {
        arcWidth: playingField.arcWidth  arcHeight: playingField.arcHeight
        width: playingField.width, height: playingField.height
        fill: Color.ROSYBROWN
        stroke: Color.DARKRED
        strokeWidth: playingField.strokeWidth
    }
    content: [
        r,
        Text {
            font : Font {
                size: 24
            }
            x: 10, y: 50
            content: "Game Over"
        },
        Text {
            font : Font {
                size: 20
            }
            x: 10, y: 80
            content: "(press Enter to restart)"
        }
   ]
}

var player: Circle = Circle {
    translateX: 250, translateY:250
    radius: 10
    fill: Color.BLACK
}

var enemies : Enemy[] = for (i in [0..3]) {
    var enemy:Enemy = Enemy {
        translateX : randomEnemyInitPosition(), translateY:randomEnemyInitPosition()
    }
    enemy
};

var timeline: Timeline = Timeline {
    repeatCount: Timeline.INDEFINITE
    keyFrames : [
        KeyFrame {
            time : 30ms
            canSkip: false
            action: function() {
                // Enemy
                for (enemy in enemies)
                    enemy.calcPosition(playingField.boundsInLocal.minX,playingField.boundsInLocal.maxX,playingField.boundsInLocal.minY,playingField.boundsInLocal.maxY);

                // Player
                if (moveRight) {
                    var newX = player.translateX + PLAYER_BALL_STEP;
                    if (newX < playingField.boundsInLocal.maxX - player.radius - playingField.strokeWidth) {
                        player.translateX = newX
                    }
                }
                if (moveLeft) {
                    var newX = player.translateX - PLAYER_BALL_STEP;
                    if (newX > playingField.boundsInLocal.minX + player.radius + playingField.strokeWidth) {
                        player.translateX = newX
                    }
                }
                if (moveUp) {
                    var newY= player.translateY - PLAYER_BALL_STEP;
                    if (newY > playingField.boundsInLocal.minY + player.radius + playingField.strokeWidth) {
                        player.translateY = newY
                    }
                }
                if (moveDown) {
                    var newY= player.translateY + PLAYER_BALL_STEP;
                    if (newY < playingField.boundsInLocal.maxY - player.radius - playingField.strokeWidth) {
                        player.translateY = newY
                    }
                }
                                // checkCollision
                for (enemy in enemies) {
                    if (checkCircleCollision(player.translateX,player.translateY,player.radius,enemy.translateX,enemy.translateY,10.0)) {
                        gameOver();
                    }
                }
            }
        }
    ]
}

// Function called after collision
function gameOver():Void {
    timeline.stop();
    mode = MODE_GAME_OVER;
    gameOverScreen.visible = true;
}

function initGame():Void {
    initActors();
    mode = MODE_RUNNING;
    gameOverScreen.visible = false
}

// Function is used to initialize the actors i.e. player and enemies
// on the playing field
function initActors() {
    player.translateX = 250;
    player.translateY = 250;
    for (enemy in enemies) {
        enemy.translateX = randomEnemyInitPosition();
        enemy.translateY = randomEnemyInitPosition();
    }
}
// http://gpwiki.org/index.php/C:Collision_detection_between_two_circles
function checkCircleCollision(c1X:Number,c1Y:Number,c1R:Number,c2X:Number,c2Y:Number,c2R:Number):Boolean {
    var distanceSquared = ((c1X - c2X) * (c1X - c2X)) +  ((c1Y - c2Y) * (c1Y - c2Y));
    var radiiSquared = (c1R + c2R) * (c1R + c2R);

    if (radiiSquared > distanceSquared) {
        return true
    }

    return false
}

function randomEnemyInitPosition():Integer {
    var r = javafx.util.Math.random();
    var r2 = javafx.util.Math.random();
    ((r  * 100)+(r2*100)) as Integer
}

Stage {
    title: "Ballgame"
    scene: Scene {
        fill: Color.CHOCOLATE;
        width: 430, height: 400
        content: Group {
            focusTraversable: true
            translateX: 10, translateY:10
            content: [
                playingField,
                player,
                enemies,
                gameOverScreen
            ]
            onKeyPressed : function (e: KeyEvent){
              if (mode==MODE_GAME_OVER) {
                if (e.code == KeyCode.VK_ENTER) {
                    initGame();
                    timeline.playFromStart();
                }
              }
              if (e.code == KeyCode.VK_LEFT) {
                    moveLeft = true;
              }
              if (e.code == KeyCode.VK_RIGHT) {
                    moveRight = true;
              }
              if (e.code == KeyCode.VK_UP) {
                    moveUp = true;
              }
              if (e.code == KeyCode.VK_DOWN) {
                    moveDown = true;
              }
            }
            onKeyReleased : function (e: KeyEvent){
              if (e.code == KeyCode.VK_LEFT) {
                    moveLeft = false;
              }
              if (e.code == KeyCode.VK_RIGHT) {
                    moveRight = false;
              }
              if (e.code == KeyCode.VK_UP) {
                    moveUp = false;
              }
              if (e.code == KeyCode.VK_DOWN) {
                    moveDown = false;
              }
            }
        }
    }
}

// Start game
timeline.playFromStart();

Enemy.fx


/*
 * Enemy.fx
 *
 * Created on 03.10.2009, 13:59:33
 */

package myballgame;

import javafx.scene.CustomNode;
import javafx.scene.Node;
import javafx.scene.shape.Circle;
import javafx.scene.paint.Color;

/**
 * @author Alexander Gnodtke
 */

public static def DEFAULT_FILL = Color.CRIMSON;

public class Enemy extends CustomNode {

    var radius = 10;
    var moveUp = false;
    var moveLeft = false;
    var speedX = 0;
    var speedY = 0;

    var enemy:Circle;

    override function create():Node {

        // Enemy Speed
        speedX = calcSpeed();
        speedY = calcSpeed();

        // Enemy initial Direction
        moveUp = calcRandomBoolean();
        moveLeft = calcRandomBoolean();

        // Enemy figure
        enemy = Circle {
            radius: radius
            fill: Color.CRIMSON
        }
        return enemy
    }

    function calcSpeed():Integer {
       var r = javafx.util.Math.random();
       if (r>0.66){
            return 3
        } else if (r<0.33) {
            return 2
        } else {
            return 1
        }
    }

    function calcRandomBoolean():Boolean {
        if (javafx.util.Math.random()>0.5)
            return true
        else
            return false
    }

    public function calcPosition(xMin:Integer,xMax:Integer,yMin:Integer,yMax:Integer) {
        if (moveUp) {
            var newY= translateY - speedY;
            if (newY > yMin + radius)
                translateY = newY
            else
                moveUp = false
        } else {
            var newY= translateY + speedY;
            if (newY < yMax - radius)
                translateY = newY
            else
                moveUp = true
        }
        if (moveLeft) {
            var newX = translateX - speedX;
            if (newX > xMin + radius)
                translateX = newX
            else
                moveLeft = false
        } else {
            var newX = translateX + speedX;
            if (newX

Creating a Simple Game in JavaFX (Part 4)

October 21st, 2009


In the previous entries we decided how our game should behave, we created the playing field and the player object. We now want to add the “enemy” objects to our game.

Creating the enemy objects

So far most of our game has been programmed in one file. For our enemies we will create a separate file and class named Enemy.fx


package myballgame;

import ...

public static def DEFAULT_FILL = Color.CRIMSON;

public class Enemy extends CustomNode {

    var radius = 10;
    var moveUp = false;
    var moveLeft = false;
    var speedX = 0;
    var speedY = 0;

    var enemy:Circle;

    override function create():Node {

        // Enemy Speed
        speedX = calcSpeed();
        speedY = calcSpeed();

        // Enemy initial Direction
        moveUp = calcRandomBoolean();
        moveLeft = calcRandomBoolean();

        // Enemy figure
        enemy = Circle {
            radius: radius
            fill: Color.CRIMSON
        }
        return enemy
    }

    function calcSpeed():Integer {
       var r = javafx.util.Math.random();
       if (r>0.66){
            return 3
        } else if (r<0.33) {
            return 2
        } else {
            return 1
        }
    }

    function calcRandomBoolean():Boolean {
        if (javafx.util.Math.random()>0.5)
            return true
        else
            return false
    }

    public function calcPosition(xMin:Integer,xMax:Integer,yMin:Integer,yMax:Integer) {
        if (moveUp) {
            var newY= translateY - speedY;
            if (newY > yMin + radius)
                translateY = newY
            else
                moveUp = false
        } else {
            var newY= translateY + speedY;
            if (newY < yMax - radius)
                translateY = newY
            else
                moveUp = true
        }
        if (moveLeft) {
            var newX = translateX - speedX;
            if (newX > xMin + radius)
                translateX = newX
            else
                moveLeft = false
        } else {
            var newX = translateX + speedX;
            if (newX

This is a big chunk of code but we will walk through it slowly.
The package definition at the top shows that this file is in the same package as our Main.fx file.
We then define the default fill color of our enemies.
Our Enemy class extends CustomNode which allows us to easily create our own Node objects to add to our Scene.
The variables radius, moveUp, moveLeft speedX, speedY and enemy will be explained later on.
The function create() defined in CustomNode must be overriden. This is were we initialize our object. We setup a random speed for our enemy in the x-direction and a different random speed for the y-direction of our enemy. We then give the enemy an initial random direction by setting moveUp and moveLeft to a random value calculated in calcRandomBoolean(). Finally we return a Node object that defines the shape of our Enemy. In our case a simple circle with a radius defined by the initialization of the radius variable.
At the end of the class we define a function that lets us calculate the Enemies new position. calcPosition(...) takes the bounds of our playing field as parameters and makes sure the new Enemy object position is within these bounds. If it is not it switches the Enemies direction. For example if the Enemy object is about to leave the playing field at the top. calcPosition(...) changes moveUp to false and in the next cycle the Enemy object will be moving downward, until it hits the lower edge of the playing field and the y-direction is again reversed.

Displaying the enemy objects

Now that we have defined an Enemy class it is time to display our enemies. This is done with the following changes to Main.fx


var player ...

var enemies : Enemy[] = for (i in [0..3]) {
    var enemy:Enemy = Enemy {
        translateX : randomEnemyInitPosition(), translateY:randomEnemyInitPosition()
    }
    enemy
};

var timeline ...

function randomEnemyInitPosition():Integer {
    var r = javafx.util.Math.random();
    var r2 = javafx.util.Math.random();
    ((r  * 100)+(r2*100)) as Integer
}

Stage {
    ...
    scene: Scene {
        ...
        content: Group {
            ...
            content: [
                playingField,
                player,
                enemies
            ]
            ...
        }
    }
}

We define an variable of type Enemy Array and initialize it with four Enemy objects. Each enemy object gets a random position within a 100 x 100 pixel area of the playing field assigned.
We then add our Enemy Array to the contents of our Group object just like we added our playing field and player.
The result looks like the following:

img4

Animating the Enemies
Now we would like to animate our enemy objects. As with the player the new enemy position is calculated with every cycle in our game play and calling the recalculate enemy position method must therefore be located in our timeline code:


var timeline: Timeline = Timeline {
    ...
    keyFrames : [
        KeyFrame {
            ...
            action: function() {
                // Enemy
                for (enemy in enemies)
                 enemy.calcPosition(playingField.boundsInLocal.minX,
                             playingField.boundsInLocal.maxX,
                             playingField.boundsInLocal.minY,
                             playingField.boundsInLocal.maxY);
            ...

So with each cycle we call the calcPosition(...) function of each player object and each enemy knows how to calculate its new position as we have seen earlier when we discussed the calcPosition(...) function.

In the next part we will deal with calculating collisions.

Creating a Simple Game in JavaFX (Part 3)

October 16th, 2009


So far we have created a simple JavaFX application which sports a rectangle shape which will be our playing field. In this part we will now add the player object and the code that lets the user control its movement.

Creating the Player Object


var player: Circle = Circle {
    translateX: 250, translateY:250
    radius: 10
    fill: Color.BLACK
}
Stage {
...
    scene: Scene {
        ...
        content: Group {
            ...
            content: [
                playingField,
                player
            ]
        }
    }
}

I will not spend a lot of time commenting the code. The player object is a simple Circle shape variable defined like the playing field at script level, i.e. outside the Stage object. The player object is then added to the content of Scene’s Group object’s content after the playing field. Adding it after the playing field makes it appear on top of the playing field otherwise it would not be visible. The result looks like this:

img3

Adding User Interaction
In order to move the player object around we will have to listen to key events. If the user presses the left arrow key, we want the player object to move left, if he presses the right arrow key, the object should move right etc.. But listening to key events is not enough. Later when we have enemy ball objects and our playing field has bounds that cannot be crossed, several actions need to be synchronized at a certain interval level. We will call this a cycle in the game play. Creating such a cycle that checks everything that has happened (for example our Key Events) and recalculates all other events (for example collisions) is very easy with a JavaFX timeline.


var timeline: Timeline = Timeline {
    repeatCount: Timeline.INDEFINITE
    keyFrames : [
        KeyFrame {
            time : 30ms
            canSkip: false
            action: function() {
            }
        }
    ]
}

Stage {
...
}

// Start game
timeline.playFromStart();

We define a timeline object that runs indefinately. Our cycle is 30ms long, so every 30ms all the game’s calculations are made. Do not forget to start the timeline at the end of our script. The function defined as the keyframe’s action is where these calculations take place as we will see in a minute.

Now for capturing the key events:


...
var moveUp = false;
var moveDown = false;
var moveLeft = false;
var moveRight = false;
...
Stage {
    ...
    scene: Scene {
        ...
        content: Group {
            focusTraversable: true
            ...
            content: [
                playingField,
                player
            ]
            onKeyPressed : function (e: KeyEvent){
              if (e.code == KeyCode.VK_LEFT) {
                    moveLeft = true;
              }
              if (e.code == KeyCode.VK_RIGHT) {
                    moveRight = true;
              }
              if (e.code == KeyCode.VK_UP) {
                    moveUp = true;
              }
              if (e.code == KeyCode.VK_DOWN) {
                    moveDown = true;
              }
            }
            onKeyReleased : function (e: KeyEvent){
              if (e.code == KeyCode.VK_LEFT) {
                    moveLeft = false;
              }
              if (e.code == KeyCode.VK_RIGHT) {
                    moveRight = false;
              }
              if (e.code == KeyCode.VK_UP) {
                    moveUp = false;
              }
              if (e.code == KeyCode.VK_DOWN) {
                    moveDown = false;
              }
            }
        }
    }
}

There are several things to notice in the new code. First of all the main Group object is set to focusTraversable=true. Otherwise it would not capture any key events. Capturing key events is pretty self explanatory except for the fact that we are not moving anything but merely remembering that the ball should move. If we press the left arrow key. The variable moveLeft is set to true. In our cycle we will have to process this information as we will see in a minute. If we release the key the variable moveLeft is set to false and the player object should not move. In theory if you were fast enough at pressing and releasing the key, a cycle could miss a movement, but this practically not happen and is not relevant. Now lets process the movement in our timeline object.


def PLAYER_BALL_STEP = 4;
...
var timeline: Timeline = Timeline {
    repeatCount: Timeline.INDEFINITE
    keyFrames : [
        KeyFrame {
            time : 30ms
            canSkip: false
            action: function() {
                // Player
                if (moveRight) {
                    var newX = player.translateX + PLAYER_BALL_STEP;
                    if (newX < playingField.boundsInLocal.maxX - player.radius - playingField.strokeWidth) {
                        player.translateX = newX
                    }
                }
                if (moveLeft) {
                    var newX = player.translateX - PLAYER_BALL_STEP;
                    if (newX > playingField.boundsInLocal.minX + player.radius + playingField.strokeWidth) {
                        player.translateX = newX
                    }
                }
                if (moveUp) {
                    var newY= player.translateY - PLAYER_BALL_STEP;
                    if (newY > playingField.boundsInLocal.minY + player.radius + playingField.strokeWidth) {
                        player.translateY = newY
                    }
                }
                if (moveDown) {
                    var newY= player.translateY + PLAYER_BALL_STEP;
                    if (newY < playingField.boundsInLocal.maxY - player.radius - playingField.strokeWidth) {
                        player.translateY = newY
                    }
                }
            }
        }
    ]
}

At the top of the script we define a constant called PLAYER_BALL_STEP which will tell us, if the player has moved the ball in one direction, how many pixel it should travel each circle. At 30ms cycle time moving just 1 pixel makes the player object very slow. This is where you can speed up and slow down the speed of the player object by adjusting the value. In a later game design you might add a speed up bonus or something similar.
Lets pretend the player has pressed the right arrow key. Variable moveRight is set to true, a new position on the x-axis is calculated for the player object by taking the current x position player.translateX and adding the number of steps the player object should move. We then check if the ball is within the bounds of the playing field, otherwise you could move the ball anywhere you wanted including right out of the window. If it is inbounds we set the X-axis position of our player object to the new value.
One thing to notice is that we are not working with if-else constructs. This is because in theory the user can press up and right at the same time and we then want the ball to move diagonally.
We now have a player object capable of moving and staying in the playing fields bounds. The next step is to add some enemy objects. Below is a complete listing of the code so far, so you don’t get mixed up.


/*
 * Main.fx
 *
 * Created on 15.10.2009, 17:36:19
 */

package myballgame;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.Group;

import javafx.scene.shape.Rectangle;

import javafx.scene.shape.Circle;

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;

import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;

/**
 * @author Alexander Gnodtke
 */
def PLAYER_BALL_STEP = 4;

var moveUp = false;
var moveDown = false;
var moveLeft = false;
var moveRight = false;

var playingField: Rectangle = Rectangle {
    arcWidth: 20  arcHeight: 20
    width: 280, height: 380
    fill: Color.ANTIQUEWHITE
    stroke: Color.DARKORANGE
    strokeWidth: 3
}

var player: Circle = Circle {
    translateX: 250, translateY:250
    radius: 10
    fill: Color.BLACK
}

var timeline: Timeline = Timeline {
    repeatCount: Timeline.INDEFINITE
    keyFrames : [
        KeyFrame {
            time : 30ms
            canSkip: false
            action: function() {
                                  // Player
                if (moveRight) {
                    var newX = player.translateX + PLAYER_BALL_STEP;
                    if (newX < playingField.boundsInLocal.maxX - player.radius - playingField.strokeWidth) {
                        player.translateX = newX
                    }
                }
                if (moveLeft) {
                    var newX = player.translateX - PLAYER_BALL_STEP;
                    if (newX > playingField.boundsInLocal.minX + player.radius + playingField.strokeWidth) {
                        player.translateX = newX
                    }
                }
                if (moveUp) {
                    var newY= player.translateY - PLAYER_BALL_STEP;
                    if (newY > playingField.boundsInLocal.minY + player.radius + playingField.strokeWidth) {
                        player.translateY = newY
                    }
                }
                if (moveDown) {
                    var newY= player.translateY + PLAYER_BALL_STEP;
                    if (newY < playingField.boundsInLocal.maxY - player.radius - playingField.strokeWidth) {
                        player.translateY = newY
                    }
                }
            }
        }
    ]
}

Stage {
    title: "Ballgame"
    scene: Scene {
        fill: Color.CHOCOLATE;
        width: 430, height: 400
        content: Group {
            focusTraversable: true
            translateX: 10, translateY:10
            content: [
                playingField,
                player
            ]
            onKeyPressed : function (e: KeyEvent){

              if (e.code == KeyCode.VK_LEFT) {
                    moveLeft = true;
              }
              if (e.code == KeyCode.VK_RIGHT) {
                    moveRight = true;
              }
              if (e.code == KeyCode.VK_UP) {
                    moveUp = true;
              }
              if (e.code == KeyCode.VK_DOWN) {
                    moveDown = true;
              }
            }
            onKeyReleased : function (e: KeyEvent){
              if (e.code == KeyCode.VK_LEFT) {
                    moveLeft = false;
              }
              if (e.code == KeyCode.VK_RIGHT) {
                    moveRight = false;
              }
              if (e.code == KeyCode.VK_UP) {
                    moveUp = false;
              }
              if (e.code == KeyCode.VK_DOWN) {
                    moveDown = false;
              }
            }
        }
    }
}

// Start game
timeline.playFromStart();

Creating a Simple Game in JavaFX (Part 2)

October 15th, 2009


In the previous entry we had looked at the game we are trying to implement including its previous implementation in JavaSE. The following is a step by step instruction how to build such a game.

Preparation

A fantastic thing about JavaFX is its official site JavaFX.com. This site is full of useful tutorials and for a change practical JavaFX example applications. Furthermore the example are updated to always run in the latest JavaFX runtime version. This is where you should go to find out more about JavaFX.  When I program I like to keep the online API docs at: http://java.sun.com/javafx/1.2/docs/api/ open.

The first thing we need to do is to create a new JavaFX project. Do this by launching Netbeans and selecting File -> New Project. In the dialog select JavaFX on the left hand side and JavaFX Script Application on the right hand side. Press “Next” and  give your project a name such as “MyBallGame”. Keep the default settings and press “Finish”.

Netbeans provides us with a Main.fx class, which is the entry point to our new application.

Creating the Background of our game

Unlike the original game I would like one area of our game to be the area of game play and I would like the score keeping to take place in a different area and not write the score onto the game area.

In JavaFX all graphics are set on a stage. This stage object requires a Scene object to be displayed. Let us change the Stage object Netbeans has provided us with to look like the following:


Stage {
    title: "Ballgame"
    scene: Scene {
        fill: Color.CHOCOLATE;
        width: 430, height: 400
        content: Group {
            content: [
            ]
        }
    }
}

This should give you something that looks like this:

img1

The code “sets the stage” by defining a stage object. The “title” is the title of the window. The initial stage needs a scene object as “content” which we create. We set the background color and the width and height. The Scene itself has a Group object as content. Group objects in JavaFX are meant to bundle several objects at once, and this will be our intention, because we will add a playing field, scorefield etc.. The Stage, which has a Scene, which has a Group object might seem overly complicated, but this construction is something we need to accept. With the Stage, Scene and Group setup, we can now add the actual parts of the game.

We create a playing field i.e. the area where the balls will move by defining a simple rectangle like this:


var playingField: Rectangle = Rectangle {
    arcWidth: 20  arcHeight: 20
    width: 280, height: 380
    fill: Color.ANTIQUEWHITE
    stroke: Color.DARKORANGE
    strokeWidth: 3
}
Stage { ... }

Notice how the variable is defined at script level outside of the Stage object. This is necessary because we need to access it later on in the code. You can look up the different Rectangle attributes in the API mentioned above. But here is a quick rundown of the attributes we have not yet encountered: arcWidth/arcHeight define the “roundness” of the rectangles corners. “stroke” and “strokeWidth” define the width and color of the surrounding border.
Now we add the “playingField” variable to the Group object we declared earlier. Because we want our playing field and all other objects to be at some distance to the windows edge we call add a “translate” transformation on the Group object, which will subsequently translate all its members in the same way.


var playingField: Rectangle = Rectangle { ... }
Stage {
    ...
    scene: Scene {
        ...
        content: Group {
           translateX: 10, translateY:10
            content: [
                playingField
            ]
        }
    }
}

This gives us the following

img2

Congratiulations we now have a smart looking playing field. One thing that becomes immediately clear is, that it is way easier creating shapes like rectangles in JavaFX than in standard Java. It is all very straight forward and a lot less boilerplate code. In the next part we will start adding the balls.

Creating a Simple Game in JavaFX (Part 1)

October 15th, 2009

A long time ago I had written a simple Java game applet with regular JavaSE and Java 2D. It was nothing fancy just something I had wanted to experiment with. I have long since lost the source code and never got around to making the game *pretty* including the introduction of game levels etc..

With a little time on my hands and looking for a fun introduction to JavaFX, I rewrote the game in JavaFX. I was also curious to find out how much easier writing the game would be using the graphic oriented JavaFX script language.

This tutorial is meant for readers with no or little prior JavaFX experience and no or little experience writting games. For an advanced tutorial on how to write a game in JavaFX please check out this article on Inside RIA

So here goes…

Installing Netbeans

As of now your best option of developing JavaFX seems to be with Netbeans. As of now 6.7.1 is the latest release with JavaFX support. Installation is pretty straight forward and simple Google search should get you any help in case you need it.

The game (as of now)

This is a short discription of the game’s rules.

You are in control of a character (currently a somewhat boring square).

You must:

  • always be on the move, otherwise you loose points
  • avoid hitting the other characters (currently somewhat boring circles), otherwise you are dead
  • touch and stay on one of the circles if it changes its color from red to green (not touching it at least once will make you loose points)

So the rules are simple enough and all other ideas and improvements will be added once the basic gameplay has been achieved. Below should be the original version of the game:

Original Game Applet (click on applet for focus and press space bar to restart)



Now you know what the game looks. like in the next entry we will start programming

Installing Apache and php on Vista

March 3rd, 2009

Yet another article explaining how to install php and Apache on Vista?

You bet!

After spending way too much time getting this done for myself and finding many incomplete solutions in the top Google picks I hope to help others with something that should not even need an explanation.

I hardly ever boot Vista but for work I was forced to setup up an Apache, php, MySQL development environment on my Vista partition. Please note: Do not follow these instructions if you are setting up a productive environment.

The first (wrong) assumption was that I download the programs and install one after the other and everything just works. First of I was not certain what to enter into the installations masks. After installing Apache I got an error along the line of:

make_sock could not bind to address 0.0.0.0:80

or

Could not reliably determine the server’s fully qualified domain name

All of the files of the php and Apache installation were not writeable. After finally getting Apache to run, I just could not figure out how to install php with many different installation instructions adding to the confusion.

While I am not sure if all of these steps are necessary, this is how it worked for me, your mileage may of course vary:

  1. Download apache (my version 2.2.11) msi file
  2. Turn off UAC (user account control) I know nothing about Vista UAC and the  implications of turning it off. Please do this at your own risk.
  3. To turn off UAC: Start-> Control Panel -> User Accounts -> User Accounts -> Turn User Account Control on or off
  4. Uncheck “Use User Account Control (UAC) to help protect your computer” -> OK
  5. Reboot
  6. Start -> All Programs -> Accessories -> Command Prompt -> Right Click -> Run as Administrator
  7. Run: ”msiexec /i C:\Users\yourusername\Documents\Downloads\apache_2.2.11-win32-x86-openssl-0.9.8i.msi” (your apache .msi file may be named differently and your download directory may be different)
  8. Enter the following (unless you know better): Network Domain = localhost, Server Name = localhost, admin email = your@email
  9. Installation directory, choose C:\Apache instead of C:\Program Files\bla bla bla\. One of the reasons is as I have been told, that the Program Files directory gives you all sorts of problems when you want to read and write your files. In my failed attempts to install php and apache, php installation was not able to write to httpd.conf and could not install, failed apache installation could not write log files and I could not update index.html.
  10. I still got a bunch of error messages after installation and could not start Apache via the Apache Service Monitor, so reboot to the rescue. After that I could start apache.

Now that Apache was installed it was time to install php:

  1. Downlaod php (my version 5.2.9) msi file
  2. Make sure UAC is still turned off (see apache installation above)
  3. Start -> All Programs -> Accessories -> Command Prompt -> Right Click -> Run as Administrator
  4. Run: ”msiexec /i C:\Users\yourusername\Documents\Downloads\php-5.2.9-win32-installer.msi” (your php .msi file may be named differently and your download directory may be different)
  5. Web Server Setup -> Choose your webserver, in my case Apache 2.2.x Module
  6. Apache Configuration Directory, in my case “C:\Apache\conf\”. In any case it is the conf directory of your apache installation.
  7. Extensions to install, in my case MySQL
  8. Installation directory, I chose C:\PHP instead of the suggested “Program Files” dir for reasons mentioned above.
  9. Strange enough, you still need to make some modifications to apache’s conf/httpd.conf file, so open it with a text editor like vim or jEdit.
  10. You will find the following lines at the end of the file:  PHPIniDir “C:/PHP/” and LoadModule php5_module “C:/PHP/php5apache2_2.dll”
  11. What is missing are the lines “AddType application/x-httpd-php .php” and “AddType application/x-httpd-php-source .phps” in the section “<IfModule mime_module>”. For example just add these lines after the entry for x-gzip. Make sure you do not have any unnecessary spaches in the mime type like I saw in one of the tutorials I googled.
  12. Reboot your machine, apache should be running.
  13. Create a file test.php in htdocs with something like <?php phpinfo(); ?> and navigate to http://localhost/test.php and you should see your php output.

I sincerely hope that I was able to help somebody save some time by giving a few pointers. Of course there are many more things that can and must be configured to get the setup just right for your purposes but the basic installation should now be working.

If you have the choice of your underlying operating system why not try to setup Apache and php on OpenSolaris or Linux? You will find installation easy by using the build in package manager, no hassle with UAC, no need for additional configuration, no non-writeable directories, no need for reboots. It is a simple, straightforward process that works out of the box.

Starting GNOME Development – Starting your own project

February 17th, 2009

I had already posted a *very* quick start guide on how to start working on existing GNOME projects. Today I will focus on how to start your own GNOME/GTK+ project.

What programming language do I use?

The mother of all questions. While you may know that you want to do GNOME/GTK+ development the choice of languages with excellent bindings is sheer overwhelming. Unfortunately it seems that picking a language and sticking with it always involves a trade off. Anyway here are a few thoughts about picking the right language just in case you can’t make the choice on your own.

C is the language most of GNOME is written in. If you want to later contribute to core GNOME projects, C is worth learning and there is no better way than writing your own little project. The drawback is the amount of boilerplate code, the need for memory management and if you come from another language the need not only to familiarize yourself with GTK+, but also with a new programming language.

Python, C#, Java, Perl, C++ all of these languages have official GNOME bindings making all of these languages a good choice. If you come from one of these languages you will probably be up and running quite quickly. Python and pygtk are widely used in GTK+ development. C# runtime is developed in the mono project and is used in several more recent GNOME projects. Java has the benefit of running on top of the very fast Java Virtual Machine (JVM). The documentation of the Java GNOME bindings are not up to par with the other languages, but that is supposed to change in the near term and might be something you can help with? The Perl GTK+ project is gtk2-perl and C++’s project is gtkmm.

Other languages. Check the official GNOME bindings for any of the other languages. You will probably do just fine using any of these if you already know the language.

Then there is Vala. Vala is a new high level language that compiles into native C code and thus will be as fast or at least almost as fast as apps written in C. Vala can therefore be used to write core GNOME libraries and apps because it does not depend on a runtime like Java, C# and Python for example do. The irc channel is always crowded so there seems to be quite some interesst in this language. Is it the future of GNOME development? I don’t know, but you should keep an eye on the project.

Summing up I would suggest to be very pragmatic with your choice. Stick to what you know and you will find success a lot quicker. After all the many GTK+ bindings have the intention of letting programmers program with whatever language they like most. If you learned the basics of GTK+ and have written your first little apps take a look at other languages. Each language has its own strengths and merits and soon you might choose a different language depending on the type of GTK+ project you are writing.

If you are new to programming and do not yet have a preferred programming language I would suggest getting started with either Python, Java or C# (I might give a slight edge to python).

Use Glade for GUI development?

So you have chosen a programming language. Now do I hand code my GUIs or do I use Glade as a graphical user interface builder? My advice would be to get started by hand coding a few of the basic widgets. Seeing the code and seeing how it works gives you a much better understanding of the GTK+ widgets and programming principles. Once you have a basic understanding of events, signals, arranging widgets etc. go ahead and start learning to use glade. For most straightforward GUIs this tool can save you a tremendous amount of time. The place to start learning to use glade is here.

I heard something about GtkBuilder instead of libglade?

Everything you need to know about this can be read Micah Carrick’s blog.

What tutorials are worth following?

  1. The best tutorial I have found to get you started quickly is Micah Carrick’s tutorial.
  2. Then there is the book Foundations of GTK+ development. So far I think it is a good resource and I will post a detailed review sometime in the near future. The only apparent problem is that the book focuses only on the C programming language and that will not be of great use if you do not plan to write your code in C.
  3. The official GTK+ tutorial is lengthy but a good starting point. Programming language is once again C.
  4. For python there is the pygtk tutorial.
  5. For Java there is this tutorial which gives you a quick overview of how to develop with javagnome bindings including using glade.
  6. For Vala there is the Vala tutorial.
  7. For C# and GTK# there are a whole bunch of articles and tutorials.

OpenSolaris Community Team on Kiva

February 17th, 2009

For those of you that do not know, Kiva is a platform that connects lenders willing to make small loans and entrepreneurs in the developing world in need of capital. With these loans these entrepreneurs are then able to expand and grow their businesses, helping themselves, their families and their community.

Kiva has introduced a feature on their site that allows lenders to group themselves around common interests by forming so called Lending Teams. As of last week there is a new OpenSolaris Lending Team on Kiva. So if you are in any way involved with OpenSolaris and would like to show your support for OpenSolaris on Kiva, please join our team.

There is also a project on OpenSolaris.org where activities and ideas can be discussed.

The OpenSolaris Lending Team was also announced on  Jim Grisanzio’s Blog and the official OpenSolaris announce mailinglist.

Loosing your Root Powers on OpenSolaris

February 10th, 2009

I am a long time Linux user who is fairly new to OpenSolaris and I was trying to set up my build environment following these instructions.

At one point you should enter the command “usermod -P “Software Installation” sfeporter”. The article created an extra user named sfeporter and I did not feel I had to bother with an extra user and could develop with my normal account. What I failed to realize and what I learned checking the man pages later on  was, that -P option “[...] replaces any existing profile setting in user_attr(4).” (from man usermod). So in fact I had crippled my account because I had lost my profile “Primary Administrator”. Of course this is purely my own fault because as the saying goes, you should always know exactly what the UNIX command you are copying and pasting really does. On the other hand I was surprised that the system would allow removing the only Primary Administrator without feedback. But I guess this is all a part of learning.

By the way you can also wreck your Primary Administrator profile by “playing around with the ‘Users and Groups’ application”.

The effect was visible on reboot with Network Connection not working, I could not run Package Updater and there was no way to Administer User and Groups, su and sudo did not work on CLI. The error message I encountered was “Roles can only be assumed by authorized users”. This Blog entry and the blog’s comments where the first stepping stone. Here is a quick rundown of what you need to do (OpenSolaris 2008.11, others will be similar):

  1. In order to restart your system in Single-User Level (Run Level S) you need to…
  2. Reboot your system
  3. If you have never run any updates and thereby created new Boot Environments (BE) choose “OpenSolaris 2008.11 snv_101b_rc2 X86 text console” from the GRUB menu, press “e” to edit and continue to step 9
  4. Else choose your active BE something like open-solaris-1  from the GRUB menu and press “e” to edit
  5. Select line splashimage /boot/solaris.xpm and press ‘d’ to remove
  6. Select line foreground d25f00 and press ‘d’ to remove
  7. Select line background 115d39 and press ‘d’ to remove
  8. Select the kernel$ line, press “e” to edit and remove “console=graphics” from the -B (Boot?) string. Now you have the equivalent of a text login.
  9. Select the kernel line (if it is not already selected) press “e” to edit and add “-s”  (Single User Level) at the end of the line.
  10. Press “b” to start booting with your new configuration.
  11. Login as root.
  12. Edit /etc/user_attr by using your favorite editor for example (vim /etc/user_attr)
  13. You should find your username at the end of the file change it to: “myusername::::type=normal;profiles=Primary Administrator;roles=root”. You may also add additional profiles comma separated after Primary Administrator. Do not add too many though, there is this bug. Adding roles=root after the profiles allows you to su (Thanks to Tom Vacek on desktop-discuss mailinglist for this hint ).
  14. If you also want to sudo you need to edit the sudoers file (This should not be related to the problem described above, I am just adding this for the sake of completness). First reboot your system. Find that you have your “Primary Administrator” powers back.  Run “pfexec visudo -s -f /etc/sudoers” and add your user name to the list of sudoers. (You may want to copy and edit the root line accordingly)

These are the instructions that got me going again, especially the part of creating a text login, otherwise you will not be able to login as root and adding the roles=root in order to su again.

My hope is that in future the Primary Administrator role cannot be discarded so easily, the bug limiting the amount of profiles is fixed and logging in in Text Mode requires only modest changes to the GRUB entry.

By the way, how should I have correctly added a new profile? In future I will simply edit /etc/usr_attr without relying on special commands or a GUI tool.