1

I'm trying my best to make a dino game replica and I'm stuck at increasing the speed of the obstacles that comes towards the character. The speed of 5 just seems to be the magic number but when I change it to six the rock doesn't get detected and ultimately doesn't spawn the next rock.

I don't know why.

there are no errors except for when the rock doesn't get spawned and I get an index out of range:

Traceback (most recent call last):
  File "C:\Users\austi\OneDrive\Desktop\scroller game\main.py", line 109, in <module>
    main()
  File "C:\Users\austi\OneDrive\Desktop\scroller game\main.py", line 102, in main
    if game.collide():
  File "C:\Users\austi\OneDrive\Desktop\scroller game\main.py", line 71, in collide
    olh = self.obstacles[0].hitBox[0]
IndexError: list index out of range

here is what I got:

main.py

import pygame
import math
import random
from player import Player
from objects import Obstacle
WIDTH, HEIGHT = 700, 400

class Game:
    def __init__(self, playerSprite=None, obSprite= None):
        self.playerSprite = playerSprite
        self.obSprite = obSprite
        self.player = Player(50, 50, 50, 50, 8, (0, 0, 0), None)
        self.obstacle = Obstacle(1, (800, 350, 50, 50), 5, None)
        self.obstacles = [self.obstacle]
        self.spawnGap = -100
        self.speed = 5
    def spawn(self):
        for obstacle in self.obstacles:
            #if its at the specific spot than spawn a rock
            if obstacle.pos.x + obstacle.w == WIDTH/2 + self.spawnGap:

                #get shape of rock
                type = round(random.randint(0, 3))

                rect = (700, 350, 50, 50)
                if self.obSprite != None:
                    self.obSprite = pygame.transform.scale(self.obSprite, (50, 50))
                if type == 1:
                    rect = (700, 350, 25, 50)
                    if self.obSprite != None:
                        self.obSprite = pygame.transform.scale(self.obSprite, (25, 50))
                if type == 2 :
                    rect = (700, 375, 50, 25)
                    if self.obSprite != None:
                        self.obSprite = pygame.transform.scale(self.obSprite, (50, 25))
                if type == 3:
                    rect = (700, 350, 75, 50)
                    if self.obSprite != None:
                        self.obSprite = pygame.transform.scale(self.obSprite, (75, 50))
                #increase the place in which the rock spawns
                self.spawnGap += 10
                #create a new obstacle and append it to the obstacle array
                self.obstacle = Obstacle(type, rect, 5, None)
                self.obstacles.append(self.obstacle)
            #delete obstacle when its at the end
            if obstacle.pos.x < -obstacle.w:
                index = self.obstacles.index(obstacle)
                del self.obstacles[index]

    def draw(self, win):
        self.spawn()
        self.hitBoxes()
        self.player.draw(window)
        for obstacle in self.obstacles:
            obstacle.draw(window)
        
    def collide(self):
        plh = self.player.hitBox[0]
        prh = self.player.hitBox[0] + self.player.hitBox[2]
        pth = self.player.hitBox[1]
        pbh = self.player.hitBox[1] + self.player.hitBox[3]
        olh = self.obstacles[0].hitBox[0]
        orh = self.obstacles[0].hitBox[0] + self.obstacles[0].hitBox[2]
        oth = self.obstacles[0].hitBox[1]
        obh = self.obstacles[0].hitBox[1] + self.obstacles[0].hitBox[3]
        if prh >= olh and plh <= orh:
            if pbh >= oth:
                return True
        else:
            return False
    def hitBoxes(self):
        key = pygame.key.get_pressed()
        if key[pygame.K_a]:
            self.player.showHitBoxes = True
            for obstacle in self.obstacles:
                obstacle.showHitBoxes = True
        if key[pygame.K_s]:
            self.player.showHitBoxes = False
            for obstacle in self.obstacles:
                obstacle.showHitBoxes = False

def main():
    done = False
    pressed = False
    game = Game()
    time = pygame.time.Clock()
    while not done:
        for event in pygame.event.get():
            if event.type == pygame.QUIT: 
                done = True
        window.fill((255, 255, 255))
        game.draw(window)
        if game.collide():
            done = True
        pygame.draw.line(window, (255, 0, 0), (WIDTH/2, 0), (WIDTH/2, HEIGHT))
        pygame.draw.line(window, (0, 0, 255), (WIDTH/2 + game.spawnGap, 0), (WIDTH/2 + game.spawnGap, HEIGHT))
        pygame.display.update()
        time.tick(60)
    pygame.quit()
main()

objects.py

import pygame
from pygame.math import Vector2

class Obstacle:
    def __init__(self, type, rect, speed, rockSprite=None):
        self.obSprite = rockSprite
        self.xSpawn = rect[0]
        self.ySpawn = rect[1]
        self.pos = Vector2(self.xSpawn, self.ySpawn)
        self.w = rect[2]
        self.h = rect[3]
        self.type = type
        self.speed = speed
        self.hitBox = ()
        self.showHitBoxes = False
    def draw(self, win):
        self.hitBox = (self.pos.x, self.pos.y, self.w, self.h)
        if self.showHitBoxes:
            pygame.draw.rect(win, (255, 0, 0), self.hitBox, 2)
        if self.obSprite != None:
            win.blit(self.obSprite, self.pos)
        else:
            pygame.draw.rect(win, (0, 0, 0), (self.pos.x, self.pos.y, self.w, self.h))
        self.update()
    def update(self):
        self.pos.x -= self.speed

player.py

import pygame
from pygame.math import Vector2
WIDTH, HEIGHT = 700, 400

class Player:
    def __init__(self, x, y, w, h, jumpVel, color, sprite=None):
        self.playerSprite = sprite
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.pos = Vector2(self.x, self.y)
        self.color = color
        self.gravity = 0.3
        self.vel = self.gravity
        self.jVel = jumpVel
        self.touching_surface = False
        self.canJump = True
        self.jumping = False
        self.hitBox = ()
        self.showHitBoxes = False
    def draw(self, win):
        self.hitBox = (self.pos.x + 0, self.pos.y + 10, 50, 30)
        if self.showHitBoxes:
            pygame.draw.rect(win, (255, 0, 0), self.hitBox, 2)
        if self.playerSprite != None:
            win.blit(self.playerSprite, self.pos)
        else:
            pygame.draw.rect(win, (255, 0, 0), (self.pos.x, self.pos.y, 50, 50))
        self.update()
    def update(self):
        mouse = pygame.mouse.get_pressed()

        if self.pos.y + self.h >= HEIGHT:
            self.touching_surface = True
            self.vel = 0
            self.pos.y = HEIGHT - self.h
        else:
            self.touching_surface = False
            self.vel += self.gravity
        if self.pos.x <= 0:
            self.pos.x = 0
        elif self.pos.x + self.w >= WIDTH:
            self.pos.x = WIDTH - self.w 

        if self.touching_surface:
            self.canJump = True
        else:
            self.canJump = False

        if mouse[0] == 1 and self.canJump and not self.jumping:
            self.jumping = True
            self.canJump = False
            self.vel = -self.jVel 
        if mouse[0] == 0:
            self.jumping = False

        self.pos.y += self.vel

and I suspect the issue is in the spawn function in the Game class in main.py

I've been trying to work on this for a couple days now and I still cannot solve my issue.

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
SoulDaMeep
  • 57
  • 6
  • I suggest reading: [How do I detect collision in pygame?](https://stackoverflow.com/questions/29640685/how-do-i-detect-collision-in-pygame/65064907#65064907) – Rabbid76 Dec 11 '21 at 21:09
  • @Rabbid76 so its not the problem with the speed but a problem with the detection? – SoulDaMeep Dec 11 '21 at 21:12
  • This is just a comment. You can simplify your code a lot. Anyway, the problem is the condition `if obstacle.pos.x + obstacle.w == WIDTH/2 + self.spawnGap`. This condition is only `True` when the obstacle is exactly at a certain position. If the speed changes, the obstacle does not exactly hit the position. – Rabbid76 Dec 11 '21 at 21:14
  • ohhh so since its going not in a multiple of 5 than its position can never be equal to the position i want it to be at? – SoulDaMeep Dec 11 '21 at 21:16
  • Yes, exactly. Test on a small range instead of a position. – Rabbid76 Dec 11 '21 at 21:20
  • it works much better but it still doesnt detect 100% of the time – SoulDaMeep Dec 11 '21 at 21:26
  • i will just detect the inbetween distance of the max speed ```py right = obstacle.pos.x + obstacle.w target = WIDTH/2 + self.spawnGap if target-5 <= right <= target+ 5 ``` and the max speed would be like 9 so its always going to be between those coordinates – SoulDaMeep Dec 11 '21 at 21:28

1 Answers1

0

The problem is the condition

if obstacle.pos.x + obstacle.w == WIDTH/2 + self.spawnGap:
   # [...]

This condition is only True when the obstacle is exactly at a certain position. If the speed changes, the obstacle does not exactly hit the position.

Test on a small range instead of a position. e.g.:

right = obstacle.pos.x + obstacle.w
target = WIDTH/2 + self.spawnGap
if traget <= right < traget + speed:
    # [...]
Rabbid76
  • 202,892
  • 27
  • 131
  • 174