Creating a Car Game in React - Part 4 - Score
Tags: react, gamedev
Published: 2019-08-25 10:19:17

---
title: Creating a Car Game in React - Part 4 - Score
published: true
description: Creating a Car Game in React - Part 4 - Score
tags: ReactJs, GameDev
---

<link rel="canonical" href="https://www.pmichaels.net/2019/06/24/creating-a-car-game-in-react-part-4-score/" />

This is the fourth part of a series (that began <a href="https://www.pmichaels.net/2019/06/03/creating-a-car-game-in-react-part-1-drawing-and-moving/">here</a>).  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 <a href="https://github.com/pcmichaels/ReactTransAm/blob/Part-Four/src/Assets/Cup.png">here</a>.  For anyone following this series, you may have noticed that my artwork is a little... crap.

The source for this post is <a href="https://github.com/pcmichaels/ReactTransAm/tree/Part-Four">here</a>.

<h1>Cups</h1>

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>
    }


<h1>Collecting cups</h1>

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);
    } 


<h1>Score</h1>

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.


<h1>References</h1>

<a href="https://www.w3schools.com/css/css3_flexbox.asp">https://www.w3schools.com/css/css3_flexbox.asp</a>

The original version of this post can be found <a href="https://www.pmichaels.net/2019/06/24/creating-a-car-game-in-react-part-4-score/">here</a>.