DEV Community

Paul Michaels
Paul Michaels

Posted on

Creating a Car Game in React - Part 4 - Score

This is the fourth part of a series (that began here). So far, we have a game where you can whizz around the screen avoiding trees. This effectively encompasses most aspects of a game of this type; that is, you can move, and there is a something preventing you.

The next step is to introduce something for the player to try to do, and give a score based on that. In our case, our player is going to try and collect cups - as per the original game. The asset I've created for the cup is here. For anyone following this series, you may have noticed that my artwork is a little... crap.

The source for this post is here.

Cups

The first thing that we've done here is added some code to place a few cups around the screen; the code for this was essentially the same as build obstacles:

placeCups() {
    let cups = [];
    const cupCount = 1;
    for (let i = 1; i <= cupCount; i++) {
        const centreX = Math.floor(Math.random() * this.state.windowWidth) + 1;
        const centreY = Math.floor(Math.random() * this.state.windowHeight) + 1; 
        cups.push(<GameItem key={i} image={cupImg} centreX={centreX} centreY={centreY} width={this.spriteWidth} height={this.spriteHeight} itemType={2} />);
    }
    return cups;
}

In a later post, I hope to do a full refactor, but for now, we have a separate function. This is rendered in the same way as the obstacles:

render() { 
    return <div onKeyDown={this.onKeyDown} tabIndex="0">
        <GameStatus Lives={this.state.playerLives} Message={this.state.message} Score={this.state.score} />

        <Background backgroundImage={backgroundImg}
    windowWidth={this.state.windowWidth} windowHeight={this.state.windowHeight} /> 

        <Car carImage={this.state.playerCrashed ? brokenCarImg : carImg} 
    centreX={this.state.playerX} centreY={this.state.playerY} 
    width={this.spriteWidth} height={this.spriteHeight} 
    rotation={this.state.playerRotation} /> 

        {this.obstacles} 

        {this.cups} 
    </div>
}

Collecting cups

In order to collect something, the player must collide with it. We need to change the collision code slightly to make it a little more re-usable:

detectAnyCollision(rect1) { 
    // Have we crashed or left the screen
    if (this.detectOutScreen(rect1)) {
        return true;
    }
    let collided = this.detectGameItemCollision(this.halfWidth, this.halfHeight, rect1, this.obstacles);
    if (collided !== undefined) {
        return true;
    }
    return false;
}

detectGameItemCollision(halfWidth, halfHeight, rect1, gameItemList) {
    const collided = gameItemList.find(a => {
        var rect2 = {
            x: a.props.centreX - halfWidth, y: a.props.centreY - halfHeight,
            width: this.spriteWidth, height: this.spriteHeight
        };
        return (this.detectCollision(rect1, rect2));
    }); 
    return collided;
}

As you can see, we now have a function that returns the item that we collided with, rather than a simple boolean. We then use this at the end of the game loop to determine whether we collided with a cup:

// Check for collected cup
const item = this.detectGameItemCollision(this.halfWidth, this.halfHeight, rect1, this.cups);
if (item !== undefined) {
    this.collectedCup(item.key);
} 

Score

There's little point in zooming around collecting cups, if there's no permanent record, so we need to add a score. Let's start with a state variable in game.jsx:

this.state = {
    playerX: 100,
    playerY: 100,
    windowWidth: 1500,
    windowHeight: 1500,
    playerMomentum: 0,
    playerRotation: 0,
    playerVelocityX: 0,
    playerVelocityY: 0,
    playerLives: 3,
    playerCrashed: false,
    gameLoopActive: false,
    message: "",
    score: 0
};

And here's the collectedCup function we mentioned a second ago:

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

All we're doing here is simply updating the score and then removing that cup from the list.

The final part is to display the score on the screen; let's have a look at our updated GameStatus.jsx:

function GameStatus(props) {
    const flexStyle = {
        display: 'flex',
        position: 'absolute',
        zIndex: 1,
        margin: 20,
        justifyContent: 'center',
        alignItems: 'center',
        width: '100%'
    };
    const labelStyle = { 
        zIndex: 1,
        margin: 50
    };
    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}>
                {props.Message}
            </label>
        </div> 
    );
}

As you can see, we're just displaying the score as part of the status.

In the next post, we'll have a look at the concept of levels, and introduce a time limit.

References

https://www.w3schools.com/css/css3_flexbox.asp

The original version of this post can be found here.

Top comments (0)