{{announcement.body}}
{{announcement.title}}

Creating a Car Game in React, Part 5: Levels and Time

DZone 's Guide to

Creating a Car Game in React, Part 5: Levels and Time

A developer continues his quest to learn React by using this popular JavaScript development framework/library to create a browser-based game.

· Web Dev Zone ·
Free Resource

This is the fifth part of a series (that began here).

In the last post, we added the concept of score. The car now can collect cups while avoiding trees; however, we don't have any concept of what happens when there are no cups left.

In this post, we'll add levels to the game, so that when you've collected all the cups, you move up. We'll also introduce a time limit to make it progressively harder (as it currently stands, it's not much of a challenge to collect the cups because you can take all day).

The source code for this post is here. Again, not everything is in the post, so please refer to the repository.

Because we are creating levels, we'll need to track the level that we're on, so a new state property is in order:

this.state = {
    playerX: 100,
    playerY: 100,
    windowWidth: window.innerWidth,
    windowHeight: window.innerHeight,
    playerMomentum: 0,
    playerRotation: 0,
    playerVelocityX: 0,
    playerVelocityY: 0,
    playerLives: 3,
    playerCrashed: false,
    gameLoopActive: false,
    message: "",
    score: 0,
    level: 1,
    cupCount: 1, 
    remainingTime: 0
};

If you've followed this through from this first post, you may be asking yourself: "Is he ever going to refactor and clean this up?!"

To which I confidently respond:

"Probably!"

Anyway, you'll notice that we have the level, the score, the time, and the cup count. Advancing through the levels is conceptually just a number; here's the code that completes a level:

completedLevel() {
    if (this.state.level >= 10) {
        this.updateMessage("Congratulations, you've completed the game");
    } 
    this.startLevel(this.state.level + 1);
}

startLevel is a slight refactor, which essentially sets the cup count and level to the new value — we'll come back to that shortly.

You can only complete a level by collecting enough cups, so the trigger should be in the cup collection:

collectedCup(key) {
    this.setState({ 
        score: this.state.score + 1 
    });
    this.cups = this.cups.filter(cup => cup.key != key);
    this.updateMessage("Collected cup");
    if (this.cups.length == 0) {
        this.completedLevel();
    } 
}

As soon as we're down to 0 cups, we call completedLevel.

Time

Now it's time to have a look at the startLevel code:

startLevel(level) { 
    this.setState({
        level: level,
        cupCount: level * 2 
    }); 
    this.obstacles = this.buildObstacles(); 
    this.cups = this.placeCups();
    this.resetCarPosition();
    this.totalLevelTimeMS = (this.TOPLEVEL - (this.state.level - 1)) * 60 * 1000
    let startLevelTimeMS = (new Date()).getTime();
    this.endLevelTimeMS = startLevelTimeMS + this.totalLevelTimeMS; 
}

We're working out when the user is out of time, and storing that in endLevelTime. Note that none of these are in state variables — the only state variable is in updated in the game loop:

let remaining = (this.endLevelTimeMS - (new Date()).getTime()) / 1000;
if (remaining <= 0) {
    this.updateMessage("Out of time!");
    this.playerDies();
}
this.setState({
    remainingTime: Math.round(remaining)
}); 

This is at the end of the game loop: we're updating the remainingTime state variable, but first, we calculate it and, if it's zero, the player dies (loses a life).

We need to tweak the code for the player dying, because otherwise the timer will never get reset:

playerDies() { 
    this.setState({
        playerLives: this.state.playerLives - 1,
        gameLoopActive: false
    });
    if (this.state.playerLives <= 0) {
        this.initiateNewGame();
    } else {
        this.startLevel(this.state.level);
    }
    this.repositionPlayer();
    this.setState({ 
        playerCrashed: false,
        gameLoopActive: true
    });
}

The last part is to make the time look a bit better with another of my patented icons. GameStatus.jsx should now return the following:

return (      
    <div className="flex-container" style={flexStyle}>
        <label style={labelStyle}>
            Lives Remaining: {props.Lives}
        </label>
        <label style={labelStyle}>
            Score: {props.Score}
        </label>
        <label style={labelStyle}>
            Level: {props.Level}
        </label>            

        <div style={containerStyle}>  
            <img src={clockImg} style={imgStyle} />
            <div style={textDivStyle}>{props.RemainingTime}</div>
        </div>

        <label style={labelStyle}>
            {props.Message}
        </label>
    </div>  
);

There are some new styles here so that the time appears over the clock icon:

const containerStyle = {
    position: 'relative',
    textAlign: 'center',
    color: 'red'
}

const textDivStyle = {        
    position: 'absolute',
    top: '50%',
    left: '50%',
    transform: 'translate(-50%, -50%)',
    zIndex: 1,
    fontWeight: 'bold'
}

const imgStyle = {
    width: '100%',
    zIndex: 0
}

In the next part, we'll implement a high score table.



If you enjoyed this article and want to learn more about React, check out this collection of tutorials and articles on all things React.

Topics:
web dev ,react.js tutorial ,react tutorial ,web application development ,game dev

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}