Collisions and score
You'll learn to check if two boxes touch, end the game when they do, and add a point for every obstacle you pass.
Watch & read
Start with the required ones. The others are there when you need them.
See it step by step
Two boxes touch only when they overlap on x and on y.
Explanation
You want the game to end when the square runs into the obstacle, and the score to go up each time you jump over one.
Think of two students sitting on one bench. They touch only if their places on the bench overlap. Our square and obstacle are boxes, so we ask that question twice: do they overlap left to right (x), and do they overlap up and down (y)? Both must be true. This kind of box check is called a bounding box collision.
function isColliding(a, b) {return (a.x < b.x + b.width && a.x + a.width > b.x &&a.y < b.y + b.height && a.y + a.height > b.y);}
The first line checks x: a starts before b ends, and a ends after b starts. The second line does the same for y. If any one is false, there is a gap, and they do not touch.
Each tick, step now does three things. If the boxes touch, it sets gameOver: true. Otherwise it
moves the obstacle. And when the obstacle goes off the left edge, the player has passed it, so score
goes up by one.
step needs the square's height, but playerY is an Animated.Value, not a plain number. So we copy the
latest number into a ref with playerY.addListener(({ value }) => (yNow.current = value)), and the loop
calls step(g, yNow.current).
People often write setScore(score + 1) inside setInterval. The timer was made on the first render,
so score inside it is always 0, and the score gets stuck at 1. Always use the version with a
function, setGame((g) => step(g, yNow.current)). It is called a functional update, and g is
always the newest game.
When you read a collision check, look for four comparisons, two for x and two for y, joined with &&.
Code tour
Click a line number with a ring to read the note for that line.
Practice
Short drills. Take your time and read the code before you answer.
1export default function App() {2 const [score, setScore] = useState(0);34 useEffect(() => {5 const id = setInterval(() => {6 // pretend the obstacle passes once every second7 setScore(score + 1);8 }, 1000);9 return () => clearInterval(id);10 }, []);1112 return <Text>{score}</Text>;13}1415// You wait 3 seconds, so the obstacle passes 3 times.
isColliding(a, b) should be true only when two boxes overlap. Each box is { x, y, width, height }, with y the height above the ground. Right now the game ends even when the square jumps right over the obstacle. Fix it.1import { useEffect, useRef, useState } from "react";2import { Animated, Pressable, StyleSheet, Text, View } from "react-native";34const SPEED = 10, SCREEN_WIDTH = 400, PLAYER_X = 50, JUMP_HEIGHT = 150;5const OBSTACLE_WIDTH = 30, OBSTACLE_HEIGHT = 50, PLAYER_SIZE = 40;6const START = { obstacleX: SCREEN_WIDTH, score: 0, gameOver: false };
Runs in the browser. Choose “My Device” in Snack to try it on your phone with Expo Go.
SPEED to 15. Can you still score 5?Quick check
3 questions. Each one explains itself after you answer.
- 1 / 3The square covers x from 50 to 90. The obstacle covers x from 100 to 130. Both are on the ground. Do they touch?
- 2 / 3The boxes overlap on x, but the square is 120 points up and the obstacle is 50 tall. What does
isCollidingsay? - 3 / 3In our game, when does the score go up?