pygame sprite numbering and sorting - arrays

I currently try to create a game with pygame, after defining the sprite Enemy, Player, and Skill, I hope the function of the skill is that the Player shoots the skill to the nearest Enemy per second, how do I get the nearest Enemy position?
While I used for loop to display the Enemy sprite in range(8) randomly from the edge of the game screen, and after one Enemy gets killed, the new enemy will be displayed. But when I use for loop, how could I know which one is sprite[1] and which one is sprite[2] followingly? Is there any way to put the sprites I created in a list or something else? So as that I can analyze which one is the nearest and let the Skill track it and kill it.
The Enemy sprite class:
`
class Enemy(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(ayu_img, (64, 64)) # sprite elements
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect() # sprite location fixed
self.radius = 32
randomNum = random.randrange(1, 4) # Dice for 1 to 4 condition
if randomNum == 1:
self.rect.x = random.randrange(-40, -32)
self.rect.y = random.randrange(0, HEIGHT - self.rect.height)
elif randomNum == 2:
self.rect.x = random.randrange(WIDTH + 32, WIDTH + 40)
self.rect.y = random.randrange(0, HEIGHT - self.rect.height)
elif randomNum == 2:
self.rect.x = random.randrange(0, WIDTH - self.rect.width)
self.rect.y = random.randrange(-40, -32)
elif randomNum == 4:
self.rect.x = random.randrange(0, WIDTH - self.rect.width)
self.rect.y = random.randrange(HEIGHT + 32, HEIGHT +40)
self.speedx = random.randrange(1, 2)
self.speedy = random.randrange(1, 2)
def update(self):
if self.rect.centerx > player.rect.centerx: # movement judge
self.rect.centerx -= self.speedx
else:
self.rect.centerx += self.speedx
if self.rect.centery > player.rect.centery:
self.rect.centery -= self.speedy
else:
self.rect.centery += self.speedy
`
The way I create enemy in game
`
for i in range(8):
enemy = Enemy() # create entity
all_sprites.add(enemy) # put into the sprite list
enemies.add(enemy) # put into the enemy list
`
is there any way to make enemy to enemy[ i ] for i in range(8)? or someway else? I am not sure if my idea is good or there is a simplified way to do it ...

A pygame.sprite.Groups can be iterated:
for sprite in all_sprites:
# [...]
of course you can enumerate the sprites:
for i, sprite in enumerate(all_sprites):
# [...]
and you can get a list of Sprites with pygame.sprite.Group.sprites:
sprite_list = all_sprites.sprites()
if len(sprite_list) > 0:
sprite_0 = sprite_list[0]
# [...]

Related

Choregraphe: How to change NAO's Chest and Feet LEDs

Pretty much what the title says. How can i change NAO's Chest and Feet LEDs. I know they support RGB but how do i command what colours they stay at?
I found a way to do it. It was actually quite simple.
I altered the code of the "Eye LEDs" box.
If you double click it it will show you 2 other boxes, "Color Edit" and "Eyes LEDs".
I simply altered the code of the second "Eyes LEDs" box with this one:
class MyClass(GeneratedClass):
def __init__(self):
GeneratedClass.__init__(self, False)
def onLoad(self):
self.fadeOps = []
self.leds = self.session().service("ALLeds")
def onUnload(self):
#~ puts code for box cleanup here
pass
def onInput_color(self, p):
if( self.getParameter("Side") == "Left" ):
sGroup = "AllLeds"
elif( self.getParameter("Side") == "Right" ):
sGroup = "AllLeds"
else:
sGroup = "AllLeds"
fadeOp = self.leds.fadeRGB(sGroup, 256*256*p[0] + 256*p[1] + p[2], self.getParameter("Duration (s)"), _async=True)
self.fadeOps.append(fadeOp)
fadeOp.wait()
self.fadeOps.remove(fadeOp)
if( self.fadeOps == [] ):
self.onDone() # activate output of the box
Technically all I did is change all the "sGroup = ... " to sGroup = "AllLeds"

Tkinter show grid on canvas

I would like to show/hide a grid on a tkinter canvas, as seen in Gimp for instance.
I have this code:
is_on = True
def my_grid():
global is_on
global hor
global ver
if is_on:
y=0
x=0
for i in range(8):
x+=64
y+=64
hor=A.create_line(0,y,512,y, fill="black")
ver=A.create_line(x,0, x, 512, fill="black")
is_on=False
else:
A.delete(hor)
A.delete(ver)
is_on=True
This delete only the last row and the last column. I've also tried to loop the delete action but it doesn't work.
Hope someone could help me.
Best
If you add a tag to the lines, you can manipulate all of the lines with a single statement.
Also, there's no need to delete and recreate the lines each time. You can hide the grid by setting their state to "hidden".
For example, here's how to create, hide, and show the grid:
def create_grid():
x = y = 0
for i in range(8):
x+=64
y+=64
A.create_line(0,y,512,y, fill="black", tags=("grid",))
A.create_line(x,0, x, 512, fill="black", tags=("grid",))
def show_grid():
A.itemconfigure("grid", state="normal")
def hide_grid():
A.itemconfigure("grid", state="hidden")
Try something like this:
import tkinter as tk
class CanvasGrid(tk.Canvas):
def __init__(self, master, **kwargs):
super().__init__(master, **kwargs)
self.lines = []
self.lines_displayed = False
def display_lines(self):
# If the lines are already displayed don't do anything
if self.lines_displayed:
return None
self.lines_displayed = True
# Clear the list of the lines as they are no longer on the screen
self.lines.clear()
# Get the width and height of the canvas
width = super().winfo_reqwidth()
height = super().winfo_reqheight()
# Loop through and create the vertical lines
for x in range(0, width-width//8, width//8):
line = super().create_line(x, 0, x, height, fill="black")
self.lines.append(line)
# Loop through and create the horizontal lines
for y in range(0, height-height//8, height//8):
line = super().create_line(0, y, width, y, fill="black")
self.lines.append(line)
def hide_lines(self):
# For each line that is on the screen
for line in self.lines:
# Delete the line
super().delete(line)
self.lines_displayed = False
# Example code that uses the class
def toggle_lines(event):
if canvas.lines_displayed:
canvas.hide_lines()
else:
canvas.display_lines()
root = tk.Tk()
canvas = CanvasGrid(root, width=400, height=400)
canvas.pack()
canvas.display_lines()
canvas.bind("<Button-1>", toggle_lines)
root.mainloop()
I created an extension to the tkinter.Canvas class by adding display_lines and hide_lines. You can use the class as if you are using tkinter.Canvas but it has those 2 methods. To try out the example code click on the canvas to show/hide the lines.
Here is another way of doing this:
from tkinter import Tk, Canvas, Button
vertical = 8
horizontal = 8
canvas_width = 700
canvas_height = 500
grid_lst = []
def grid(mode):
if mode == 'show':
if len(grid_lst):
return
for hor in range(horizontal):
y = (hor + 1) * (canvas_height / (horizontal + 1))
grid_lst.append(canvas.create_line((0, y, canvas_width, y), fill='black'))
for ver in range(vertical):
x = (ver + 1) * (canvas_width / (vertical + 1))
grid_lst.append(canvas.create_line((x, 0, x, canvas_height), fill='black'))
if mode == 'hide':
for line in grid_lst:
canvas.delete(line)
grid_lst.clear()
root = Tk()
canvas = Canvas(root, width=canvas_width, height=canvas_height, bg='grey')
canvas.pack()
Button(root, text='Show Grid', command=lambda: grid('show')).pack(side='left', fill='x', expand=True)
Button(root, text='Hide Grid', command=lambda: grid('hide')).pack(side='right', fill='x', expand=True)
root.mainloop()
This method uses just one function that has an argument which determines what to do with grid: either show or hide. It also uses variables in a way that changing them would also auto-adjust the grid: vertical, horizontal, canvas_width, canvas_height. And they do what they say. vertical determines the amount of vertical lines and horizontal determines the amount of horizontal lines so this is pretty adjustable and "dynamic".
EDIT: improved performance so that lines don't add up to the list if the grid('show') is called multiple times and so that the list gets cleared when grid('hide') is called.

How can I get enemies to spawn in Godot and be attracted/travel to a static point?

I am having trouble with path2Ds. So the enemies spawn at intervals as per the "your first game". However, what I want is for them to spawn and move towards/go to a stationary character or a set of coordinates from wherever they spawn.
I have tried a bunch of stuff and can't get it to work. Any ideas?
Code would be appreciated.
(If it makes any difference the player will use a centre of gravity to attract the enemies of their path - would I need to define a new path so that they don't continue to go back to the hub immediately, but after a certain time they would?)
func _on_MobTimer_timeout():
$Path2D/PathFollow2D.offset = randi()
var mob = Enemy.instance()
add_child(mob)
var direction = $Path2D/PathFollow2D.rotation + PI / 2
mob.position = $Path2D/PathFollow2D.position
mob.linear_velocity = Vector2(rand_range(mob.min_speed, mob.max_speed), 0)
mob.linear_velocity = mob.linear_velocity.rotated(direction)
Thanks.
Since you don't need pathfinding this is how I would probably do it:
In your game script have something like:
onready var player := $Player
func _on_SpawnEnemyTimer_timeout():
var enemy = Enemy.instance() as Enemy
enemy.player = player
add_child(enemy)
In your enemy script have something like:
export var speed := 10.0
# you can assign this when you spawn your enemies
var player: Node2D
func _physics_process(delta: float):
add_central_force(global_position.direction_to(player.global_position) * speed)
Does that work?

Godot 3.1 Invalid get'index '16' (on base:Array)

Invalid get'index '16' (on base:Array).
Hi Im getting the above message when the player attempts to moves outside of the
array size (tile size).
The error occurs on line 54:
grid[new_grid_pos.x][new_grid_pos.y] = ENTITY_TYPES.PLAYER
Im stuck on how to fix it.
regards
Jason
extends TileMap
var tile_size = get_cell_size()
var half_tile_size = tile_size / 2
enum ENTITY_TYPES {PLAYER, OBSTACLE, COLLECTIBLE}
var grid_size = Vector2(16,16)
var grid = []
onready var Obstacle = preload("res://scenes/Obstacle.tscn")
func _ready():
# Creates grid array of grid_size
for x in range(grid_size.x):
grid.append([])
for y in range(grid_size.y):
grid[x].append(null)
print(ENTITY_TYPES)
# Create obstacle positions array
var positions = []
# create 5 obstacles
for n in range(5):
# random positions constrained to grid_size
var grid_pos = Vector2(randi() % int(grid_size.x),randi() % int(grid_size.y))
#check random posisitions not already in array before adding new one
if not grid_pos in positions:
positions.append(grid_pos)
for pos in positions:
var new_obstacle = Obstacle.instance()
new_obstacle.position = (map_to_world(pos) + half_tile_size)
grid[pos.x][pos.y] = ENTITY_TYPES.OBSTACLE
add_child(new_obstacle)
func is_cell_vacant(pos, direction):
# Return true if cell is vaccant, else false
var grid_pos = world_to_map(pos) + direction
# world Boundaries
if grid.pos.x < grid_size.x and grid_pos.x >= 0:
if grid.pos.y < grid_size.y and grid_pos.y >= 0:
#return true if grid[grid_pos.x][grid_pos.y] == null else false
return grid[grid_pos.x][grid_pos.y] == null
return false
func update_child_pos(child_node):
# Move a child to a new position in the grid array
# Returns the new target world position of the child
var grid_pos = world_to_map(child_node.position)
grid[grid_pos.x][grid_pos.y] = null
var new_grid_pos = grid_pos + child_node.direction
grid[new_grid_pos.x][new_grid_pos.y] = ENTITY_TYPES.PLAYER
var target_pos = (map_to_world(new_grid_pos) + half_tile_size)
return target_pos
pass
You are receiving the error because you are exceeding the arrays elements.
To avoid it, either make the grid bigger or make it so that the player cannot leave the grid.
If you want the player to be able to leave the grid, then you will probably have to create an if condition:
if player_in_grid: grid[new_grid_pos.x][new_grid_pos.y] = ENTITY_TYPES.PLAYER.

How to create an array of SKSpriteNodes using Swift?

I've designed my program so that each time the user touches the screen, the sprite's image and position changes. I want to be able to make an array of SKSpriteNodes. I've seen a similar post, but they used a for-in loop. Is it possible to create a SKSpriteNode at initialization?
GameScene: SKScene {
// make an array of images that you will possibly change in the future
// calls the image
let dad = SKSpriteNode(imageNamed: "dad0")
var imageName = "dad"
let moveLeft = SKAction.moveByX(-10, y:0 , duration: 0.01)
...assuming that I already placed dad sprite node onto screen...
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
// when index is zero, sprite is dad0.
// Change image to dad1 (lifted leg), don't change position
if index == 0{
index += 1 //image names are dad0, dad1
imageName += "\(index)"
print(imageName)
dad.texture = SKTexture(imageNamed:imageName)
}
else{
index += 1
imageName += "\(index)"
print(imageName)
dad.texture = SKTexture(imageNamed:imageName)
//moves dad
dad.runAction(moveLeft) // moves image
index = 0
}
//change the image name back to dad
imageName = "dad"
}
}
An array of SKSpriteNode could be simply: [SKSpriteNode] (Array in swift composed by elements of SKSpriteNode type)
So , everytime you want to add a new SKSpriteNode you can do it with:
var arraySprites :[SKSpriteNode] = [SKSpriteNode]()
let dad : SKSpriteNode!
dad = SKSpriteNode(imageNamed: "dad0")
arraySprites.append(dad)

Resources