Akhil Anand Sirra
Akhil Anand Sirra

Reputation: 59

Generate enemies around the player from all directions randomly

The enemy are being generated from above the screen and then move toward player in the middle, I want to generate enemies randomly around the screen from all directions but not inside the screen directly and proceed to move towards the player and also enemy sprites are sometimes joining combining and moving together how to repel the enemy sprites.

I have tried changing x,y coordinates of enemy objects using a random range but sometimes they generate objects inside the play screen, I want enemies to generate outside the playing window.

class Mob(pg.sprite.Sprite):
    def __init__(self):
        pg.sprite.Sprite.__init__(self)
        self.image = pg.image.load('enemy.png').convert_alpha()
        self.image = pg.transform.smoothscale(pg.image.load('enemy.png'), (33, 33))
        self.image_orig = self.image.copy()
        self.radius = int(29 * .80 / 2)
        self.rect = self.image.get_rect()
        self.rect.x = random.randrange(width - self.rect.width)
        self.rect.y = random.randrange(-100, -40)
        self.speed = 4
        self.rot = 0
        self.rot_speed = 5
        self.last_update = pg.time.get_ticks()

    def rotate(self):
        now = pg.time.get_ticks()
        if now - self.last_update > 50:
            self.last_update = now
            self.rot = (self.rot + self.rot_speed) % 360
            new_image = pg.transform.rotozoom(self.image_orig, self.rot, 1)
            old_center = self.rect.center
            self.image = new_image
            self.rect = self.image.get_rect()
            self.rect.center = old_center

    def update(self):
        self.rotate()
        dirvect = pg.math.Vector2(rotator.rect.x - self.rect.x,
                                  rotator.rect.y- self.rect.y)
        if dirvect.length_squared() > 0:
            dirvect = dirvect.normalize()

            # Move along this normalized vector towards the player at current speed.
        if dirvect.length_squared() > 0:
            dirvect.scale_to_length(self.speed)
        self.rect.move_ip(dirvect)
        if self.rect.top > height + 10 or self.rect.left < -25 or self.rect.right > width + 20:
            self.rect.x = random.randrange(width - self.rect.width)
            self.rect.y = random.randrange(-100, -40)
            self.speed = random.randrange(1, 4)

[UPDATE] This the remaining code:

import math
import random
import os
import pygame as pg
import sys

pg.init()
height = 650
width = 1200

os_x = 100
os_y = 45
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (os_x, os_y)

screen = pg.display.set_mode((width, height), pg.NOFRAME)
screen_rect = screen.get_rect()
background = pg.image.load('background.png').convert()
background = pg.transform.smoothscale(pg.image.load('background.png'), (width, height))
clock = pg.time.Clock()
running = True

font_name = pg.font.match_font('Bahnschrift', bold=True)


def draw_text(surf, text, size, x, y, color):
   [...]


class Mob(pg.sprite.Sprite):
[...]    

class Rotator(pg.sprite.Sprite):
    def __init__(self, screen_rect):
        pg.sprite.Sprite.__init__(self)
        self.screen_rect = screen_rect
        self.master_image = pg.image.load('spaceship.png').convert_alpha()
        self.master_image = pg.transform.smoothscale(pg.image.load('spaceship.png'), (33, 33))
        self.radius = 12
        self.image = self.master_image.copy()
        self.rect = self.image.get_rect(center=[width / 2, height / 2])
        self.delay = 10
        self.timer = 0.0
        self.angle = 0
        self.distance = 0
        self.angle_offset = 0

    def get_angle(self):
        mouse = pg.mouse.get_pos()
        offset = (self.rect.centerx - mouse[0], self.rect.centery - mouse[1])
        self.angle = math.degrees(math.atan2(*offset)) - self.angle_offset
        old_center = self.rect.center
        self.image = pg.transform.rotozoom(self.master_image, self.angle, 1)
        self.rect = self.image.get_rect(center=old_center)
        self.distance = math.sqrt((offset[0] * offset[0]) + (offset[1] * offset[1]))

    def update(self):
        self.get_angle()
        self.display = 'angle:{:.2f} distance:{:.2f}'.format(self.angle, self.distance)
        self.dx = 1
        self.dy = 1
        self.rect.clamp_ip(self.screen_rect)

    def draw(self, surf):
        surf.blit(self.image, self.rect)

    def shoot(self, mousepos):
        dx = mousepos[0] - self.rect.centerx
        dy = mousepos[1] - self.rect.centery
        if abs(dx) > 0 or abs(dy) > 0:
            bullet = Bullet(self.rect.centerx, self.rect.centery, dx, dy)
            all_sprites.add(bullet)
            bullets.add(bullet)

Upvotes: 1

Views: 130

Answers (1)

jaaq
jaaq

Reputation: 1256

There's not much informations to go by here, but you probably need to check the x and y range your play window has and make sure the random spawn coordinates you generate are outside of it:

In your init:

# These are just example min/max values. Maybe pass these as arguments to your __init__ method.
min_x = min_y = -1000
max_x = max_y = 1000
min_playwindow_x = min_playwindow_y = 500
max_playwindow_x = max_playwindow_y = 600

self.x = (random.randrange(min_x, min_playwindow_x), random.randrange(max_playwindow_x, max_x))[random.randrange(0,2)]

self.y = (random.randrange(min_y, min_playwindow_y), random.randrange(max_playwindow_y, max_y))[random.randrange(0,2)]

This solution should work in basically any setup. For x and y it generates a tuple of values outside the playing window. Then a coinflip decides on the value. This will only spawn mobs that are diagonally outside the playing field, but it will always generate valid random coordinates.

Another approach would be just generating as many random variables as needed to get a valid pair like this:


while min_playingwindow_x <= self.x <= max_playingwindow_x and 
      min_playingwindow_y <= self.y <= max_playingwindow_y:
    # While within screen(undesired) calculate new random positions
    self.x = random.randrange(min_x, max_x)
    self.y = random.randrange(min_y, max_y)

This can be really slow however if your valid amount of positions is (for example) only 1% of the total positions.

IF you need something really fleshed out, you need to know the corners of both your map and the rectangle that is actually displayed, which is I assume smaller than the entire map(otherwise you cannot spawn enemies outside your view.

(0,0)
+----------------------+
|           A          |
|-----+-----------+----|
|  D  |      W    | B  |
|-----+-----------+----|
|          C           |
+----------------------+(max_x, max_y)

In this diagram W is the window that is acutally visible to the player, and A,B,C,D together are the part of your map that is not currently visible. Since you only want to spawn mobs outside the player's view, you'll need to make sure that the coordinates you generate are inside your map and outside your view:

def generate_coordinates_outside_of_view(map_width=1000, map_height=1000, view_window_top_left=(100, 100),
                                         view_width=600, view_height=400):
    """
    A very over the top way to generate coordinates outside surrounding a rectangle within a map almost without bias
    :param map_width: width of map in pixels (note that 0,0 on the map is top left)
    :param map_height: height of map in pixels
    :param view_window_top_left: top left point(2-tuple of ints) of visible part of map
    :param view_width: width of view in pixels
    :param view_height: height of view in pixels
    """
    from random import randrange

    # generate 2 samples for each x and y, one guaranteed to be random, and one outside the view for sure.
    x = (randrange(0, map_width), (randrange(0, view_window_top_left[0]),
          randrange(view_window_top_left[0] + view_width, map_width))[randrange(0, 2)])
    y = (randrange(0, map_height), (randrange(0, view_window_top_left[1]),
          randrange(view_window_top_left[1] + view_height, map_height))[randrange(0, 2)])

    # now we have 4 values. To get a point outside our view we have to return a point where at least 1 of the
    # values x/y is guaranteed to be outside the view.

    if randrange(0, 2) == 1:  # to be almost completely unbiased we randomize the check
        selection_x = randrange(0, 2)
        selection_y = randrange(0, 2) if selection_x == 1 else 1
    else:
        selection_y = randrange(0, 2)
        selection_x = randrange(0, 2) if selection_y == 1 else 1

    return x[selection_x], y[selection_y]

HTH

Upvotes: 2

Related Questions