DEV Community

Paul Michaels
Paul Michaels

Posted on

2

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.

Image of Datadog

How to Diagram Your Cloud Architecture

Cloud architecture diagrams provide critical visibility into the resources in your environment and how they’re connected. In our latest eBook, AWS Solution Architects Jason Mimick and James Wenzel walk through best practices on how to build effective and professional diagrams.

Download the Free eBook

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more