Reputation: 59
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
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