Tag Archives: CSS

Creating a Car Game in React – Part 5 – Levels and Time

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 for this post is here. Again, not everything is in the post, so please refer to the repository.

Levels

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.

Create CSS effect to “Shine” a button border

Imagine that you have an HTML button or element on a page and you would like an effect where the border shines all around the perimeter. this provides an excellent example of an effect of the entire element shining, and this post will largely be based on that code.

Animations

CSS has the concept of an animation, to define it, use the following syntax:

.growOnHover:hover:after {
    animation: growAnimation 1s;
}

Here is the HTML referencing this:

<a href="#" class="growOnHover">Grow</a>

Tge “growAnimation” refers to a KeyFrame:

@keyframes growAnimation {
    from {width: 100px; height: 100px;}
    to {width: 110px; height: 110px;}
}

The effect

The effect that I want is for a light to run around the circumference of the button when it’s hovered over. In this case, instead of animating from .. to, we can specify at which stage a particular section of the animation kicks in.

.borderShine:after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 5;
  height: 5;
  opacity: 0;  

  border-radius: 1;

  background: rgba(255, 255, 255, 10);
}

.borderShine:hover:after {
  animation: shineAnimation 2s 1;  
}

@keyframes shineAnimation {
  0%   {left: 0; top: 0; width: 2; height: 2; opacity: 0}
  10%  {width: 100; height: 2}  
  20%  {left: 98; top: 0; width: 2; height: 2}
  25%  {opacity: 1;}
  30%  {height: 100}
  40%  {left: 98; top: 98; height: 2}
  50%  {left: 0; top: 98; width: 100}
  55%  {opacity: 1;}
  60%  {left: 0; top: 98; width: 2; height: 2}
  70%  {left: 0; top: 0; width: 2; height: 100}
  80%  {left: 0; top: 0; width: 2; height: 2}
  100% {opacity: 0;}
}

There are a few useful things to remember here:

  • The animation is a transition between the state that the screen is currently in, and the state that you want it to be in; so, for example, the opacity set to 1 at 25% will cause the white bar to gradually appear over the steps between the two. The reason that I’ve set opacity twice here is to prevent it from transitioning back too soon.
  • All the figures above are absolute (as my buttons are 100 x 100).

References

http://jsfiddle.net/AntonTrollback/nqQc7/

https://css-tricks.com/useful-nth-child-recipies/

https://css-tricks.com/using-multi-step-animations-transitions/

CSS Overlaying Controls (absolute and relative positioning)

Having looked at CSS in the past, and thought that it’s probably something that people who are better at UI design that me should concern themselves with, I’ve recently been playing with it while looking at the new Dot Net Core web apps.

The problem that I’m looking at in this particular article is how to overlay one control on top of another. I have no doubt that there are dozens of possibilities; but the two specific ones that I’ll be focusing on are positioning absolute and relative.

Target

The idea here is for a web-page that looks like this:

Target Layout

HTML

The HTML is pretty basic for this:

<body>
  <form>
    <div>
      <img src=""  />
    </div>
    <div class="overlay">
      <input type="text" name="destination" />
    </div>
  </form>
</body>

CSS: absolute positioning

We have the basic elements, so now it’s down to CSS to make the screen above. By default, web browsers will render the div’s sequentially, and so the input box will appear below the image.

One possibility is to use “absolute” positioning. This means that I can position an element without regard to where other elements on the page might be; here’s an example:

.overlay {
    position: absolute; 
    top: 30%;         
    text-align: center;
    z-index: 10;
}

img {
  background-color: blue;
  width: 100%;
  height: 500px;
}

I’ve used 30% here because it matters on the size of your viewport – so that’s not ideal. Also, the centre align doesn’t work. This kind of makes sense when you think about it, because you’re using an absolute position – so what do you want to centralise it to?

CSS: relative positioning

Relative positioning took me a while to work out. It sounds like it’s relative to something else – but it’s actually relative to itself. Here’s what I tried for relative positioning:

.overlay {
    position: relative;
    width: 80%;
    height: 35px;
    top: -50px;
    border: none;
    text-align: center;
    z-index: 10;    
}

As you can see, it’s top position is negative, so it moves up from where it would have been. Also, because the positioning is relative, the centre align now works, because it’s back in the flow of the page.

References

https://css-tricks.com/absolute-relative-fixed-positioining-how-do-they-differ/

https://www.w3.org/Style/Examples/007/center.en.html#block