Through using VPython, I am able to get the program I am working on to generate multiple balls by calling the same class. I am also able to have the balls appear within a selected random range when they are generated (across x, y and z).
However, I am currently stumped on how I get to call the pos / position function from within my loop - as I would like to have the balls move.
Please see my code so far below.
If I call Ball.pos it states as undefined, but if I place my positioning via self.position, only one ball is generated, as they are not being referred to from under the sphere details?
from visual import *
from random import *
scene.title = "Multi Balls"
wallBm = box(pos=(0,-6,0), size=(12.2,0.1,12.1), color=color.blue, opacity=0.4)
vscale = 0.1
deltat = 0.005
t = 0
scene.autoscale = False
i = 0
totalBalls = 10
class Ball:
def __init__(self):
self.velocity = vector(0,5,0)
#vel sample ([5,10,15,20,25],3)
sphere (pos=(randrange (-6,6),randrange (-6,6),randrange (-6,6)), radius=0.5, color=color.cyan)
while True:
rate(100)
if i < totalBalls:
Ball()
i = i + 1
t = 5 + deltat
Try Inheritance from frame:
class Ball(frame):
def __init__(self, pos=(randrange (-6,6),randrange (-6,6),randrange (-6,6))):
frame.__init__(self,pos=pos)
self.velocity = vector(0,5,0)
sphere(frame=self,pos=pos,radius=0.5, color=color.cyan)
listOfBalls=[]
while True:
rate(100)
for i in range(totalBalls):
listOfBalls.append(Ball())
now try again!
You can call each Ball's position by calling listOfBalls[3].pos. I hope this helps!
Related
I want to build some visualizations for searching algorithms (BFS, A* etc.) within a grid.
My solution should show each step of the algorithm using CodeSkulptor simplegui (or the offline version using SimpleGUICS2Pygame.)
I've made a version which highlights all the cells visited by changing their color, but I've run into trouble trying to make the path display step-by-step with a time delay between each step.
I've extracted the essence of the problem and created a minimal example representing it in the code below, also run-able online here: http://www.codeskulptor.org/#user47_jB2CYfNrH2_2.py
What I want is during the change_colors() function, for there to be a delay between each iteration.
CodeSkulptor doesn't have time.sleep() available, and I don't think it would help anyway.
CodeSkulptor does have timers available, which might be one solution, although I can't see how to use one in this instance.
Code below:
import time
try:
import simplegui
except ImportError:
import SimpleGUICS2Pygame.simpleguics2pygame as simplegui
simplegui.Frame._hide_status = True
TITLE = "TEST"
FRAME_WIDTH = 400
FRAME_HEIGHT = 400
DELAY = 10
class Square:
"""This class represents a simple Square object."""
def __init__(self, size, pos, pen_size=2, pen_color="red", fill_color="blue"):
"""Constructor - create an instance of Square."""
self._size = size
self._pos = pos
self._pen_size = pen_size
self._pen_color = pen_color
self._fill_color = fill_color
def set_color(self, color):
self._fill_color = color
def get_color(self):
return self._fill_color
def is_in(self, pos):
"""
Determine whether coordinates are within the area of this Square.
"""
return self._pos[0] < pos[0] < self._pos[0] + self._size and self._pos[1] < pos[1] < self._pos[1] + self._size
def draw(self, canvas):
"""
calls canvas.draw_image() to display self on canvas.
"""
points = [(self._pos[0], self._pos[1]), (self._pos[0] + self._size, self._pos[1]),
(self._pos[0] + self._size, self._pos[1] + self._size), (self._pos[0], self._pos[1] + self._size)]
canvas.draw_polygon(points, self._pen_size, self._pen_color, self._fill_color)
def __str__(self):
return "Square: {}".format(self._pos)
def draw(canvas):
for square in squares:
square.draw(canvas)
def change_colors():
for square in squares:
# time.sleep(1) # Not implemented in CodeSkulptor and would'nt work anyway
square.set_color("green")
frame = simplegui.create_frame(TITLE, FRAME_WIDTH, FRAME_HEIGHT)
frame.set_draw_handler(draw)
width = 20
squares = []
for i in range(10):
squares.append(Square(width, (i * width, 0)))
change_colors()
frame.start()
Any help appreciated.
Yes, you need to use a timer. Something like this:
I = 0
def change_next_color():
if I < len(squares):
squares[I].set_color("green")
global I
I += 1
timer = simplegui.create_timer(1000, change_next_color)
timer.start()
http://www.codeskulptor.org/#user47_udyXzppCdw2OqdI.py
I also replaced
simplegui.Frame._hide_status = True
by simplegui.Frame._hide_controlpanel = True
https://simpleguics2pygame.readthedocs.io/en/latest/simpleguics2pygame/frame.html#SimpleGUICS2Pygame.simpleguics2pygame.frame.Frame._hide_controlpanel
See also _keep_timers option of SimpleGUICS2Pygame to help you:
https://simpleguics2pygame.readthedocs.io/en/latest/simpleguics2pygame/frame.html#SimpleGUICS2Pygame.simpleguics2pygame.frame.Frame._keep_timers
Possible improvements:
Find a better solution that don't use a global counter.
Stop timer when all work is finished.
I am trying to move only one sprite in a game loop including two sprites. I have included spr1 in the Bit.update() method so if w is pressed spr1.y -= 60. In other words the sprite is assigned to self in the hope that only spr1 will be moved when w is pressed. However, in the current code, pressing w moves both spr1 and spr2. How can I refer to and update only spr1 in the update method?
class Bit(games.Sprite):
def update(self):
if games.keyboard.is_pressed(games.K_w):
self = spr1
self.y -= 60
def main():
spr1_image = games.load_image("spr1.png")
spr1 = Bit(image = spr1_image,
x = games.screen.width / 10,
y = games.screen.height / 10)
spr2_image = games.load_image("spr2.png")
spr2 = Bit(image = spr2_image,
x = games.screen.width * 9 / 10,
y = games.screen.height * 9 / 10)
games.screen.add(spr1)
games.screen.add(spr2)
games.screen.mainloop()
main()
Edited
The two sprites have different behavior, so you need different classes for them.
In Bit, take out the self=spr1 line and you should be OK. In update(), self is whatever the current sprite is - spr1 or spr2.
In main, change spr2 to
spr2 = games.Sprite(image = spr2_image, # <---- changed class
x = games.screen.width * 9 / 10,
y = games.screen.height * 9 / 10)
Since spr2 doesn't need to respond to the keyboard, it doesn't need any behaviour that games.Sprite doesn't already provide. Therefore, you don't need to use Bit for it.
(Caveat: I haven't used livewires, so I may be missing something obvious :) )
I'm in the process of creating a basic game in pygame at the moment, and one part of that is the procedural generation of new areas as you go off the screen. As a test, I'm looking to generate an object once per area by defining its variables, and then save that area's object within the class for if you come back to it later. Here's what I have at the moment:
#area_gen is set to "true" if you move to a new area
#swidth and sheight are set to the size of the screen
#x_area and y_area are defined as you change areas, acting as sector coordinates
#Red is defined in globals
class areas:
def __init__(self, coords):
self.coordinates = coords
self.generated = False
def gen_objects(self):
if not self.generated:
self.objs = []
obj_type = "test object"
center_x = random.randrange(105, swidth-25)
center_y = random.randrange(25, swidth - 175)
self.objs.append([obj_type, center_x, center_y])
self.generated = True
#Within The Game Loop
if area_gen == "true":
coords = str(str(x_area) + " " + str(y_area))
area = areas(coords)
area.gen_objects()
for thing in area.objs:
if thing[0] == "test object":
pygame.draw.rect(screen, Red, (thing[1], thing[2], 250, 250))
#Bottom of the Game Loop
area_gen = "false"
What I thought the self.generated variable would do was stop the new object generation if one already existed, but that doesn't seem to be working. The square still generates at a new location even if the area has already been visited. My knowledge on classes is relatively limited, so I'm a bit stuck as to where to go from here.
Pass area_gen into the constructor for the areas class, and set self.generated = area_gen. Does this work? I can't see enough of your code to know, to be honest.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
This is the vector2 module:
class Vector2(object):
"this calculates the vector between two points"
def __init__(self , x = 0.0, y = 0.0):
self.x = x
self.y = y
def __str__(self):
return "( %s,%s )"%(self.x, self.y)
#classmethod
def from_points(cls,p1,p2):
return cls(p2[0]-p1[0],p2[1]-p1[1])
#the magnetude of a vector is the distance between two points
def get_magnetude(self):
return math.sqrt(self.x**2+self.y**2)
def normalize(self):
magnetude = self.get_magnetude()
self.x /= magnetude
self.y /= magnetude
#rhs stands for right hand side
def __add__(self,rhs):
return Vector2(self.x + rhs.x,self.y+rhs.y)
def __sub__(self,rhs):
return Vector2(self.x-rhs.x,self.y-rhs.y)
def __neg__(self):
return Vector2(-self.x, -self.y)
def __mul__(self,scalar):
return Vector2(self.x*scalar,self.y*scalar)
def __div__(self,scalar):
return Vector2(self.x /scalar, self.y/scalar)
And this is the main program, which imports vector2:
background_image_file = 'download.jpeg'
sprite_image_file = 'images.jpeg'
import math
import pygame
from pygame.locals import*
from sys import exit
import vector2
pygame.init()
screen = pygame.display.set_mode((640,480), 0 ,32)
background = pygame.image.load(background_image_file).convert()
sprite = pygame.image.load(sprite_image_file).convert_alpha()
clock = pygame.time.Clock()
position = Vector2(100.0, 100.0)#the starting point coordonates
heading = Vector2()#a vector without its magnetude(unit vector)
speed = 250.0
running = True
while running:
for event in pygame.event.get():
if event.type == QUIT:
exit()
if event.type == MOUSEBUTTONDOWN:
destination_x = event.pos[0]-sprite.get_width()/2
destination_y =event.pos[1]-sprite.get_height()/2
destination = (destination_x, destination_y)
heading = Vector2.get_magnetude(position,destination)#
heading.normalize()#a vector without magnetude,just direction
screen.blit(background, (0,0))
screen.blit(sprite, position)
time_passed = clock.tick()
time_passed_seconds = time_passed / 1000.0
distance_moved = time_passed_seconds * speed
position += (heading * distance_moved)
pygame.display.update()
I am learning Python and pygame by my self with (Beginning Game Development with Python and Pygame - From Novice to Professional (2007)) and I can't get the program to work. Also can someone please explain to me why the author use position = Vector2(100.0,100.0) and position = Vector2() to create new vectors?
It keeps saying :
traceback (most recent call last):
File "/home/moussa/Documents/python /vector_movement.py", line 21, in <module>
position = Vector2(100.0, 100.0)#the starting point coordonates
NameError: name 'Vector2' is not defined
I'm guessing that the code in the question is actually split between two files, one with the Vector2 class (in vector2.py) and the rest in some other file (which imports vector2).
The issue you're running into is that you're not accessing the class correctly. In your main module, you need to use vector2.Vector2 to access the class within its module.
Or, if you'd prefer to have easier access to the class, you could instead change your import from import vector2 to from vector2 import Vector2. This puts the class into the main module's namespace, so you can access it directly as Vector2.
As for the use of position = Vector2(100.0,100.0), this is a call to the Vector2 class's constructor. It creates an instance of the class, initializes it with the values 100 for x and 100 for y, and then binds it to the variable position. You can then use the various methods and operators that the class defines to update the instance, or get new values. For instance, the later line position += (heading * distance_moved) first multiplies the vector heading by the scalar distance_moved, then adds the vector result to position. You could do this with values in lists or tuples, but it would be much more complicated (you'd need to add and multiply the components of the vectors yourself).
I have a ball generator, that "generates" and adds balls(circles) to the simulation.
The ball is to be removed when it hits a static poly in list s_boxes.
This is done by a collision handler ball_wall_collision.
The Error:
The following pop-up window does what it's name says, it pops-up
My code:
Ball Generator
class BallGenerator:
def __init__(self, min_y, max_y, x):
self.min = min_y
self.max = max_y
self.x = x
self.counter = 0
def bowl(self, balls):
global ball_bowled
y = random.randint(self.min, self.max)
pos = to_pymunk((self.x,y))
r = 10
m = 15
i = pm.moment_for_circle(m, 0, r)
b = pm.Body(m,i)
b.position = pos
f_x = random.randint(-600000,-400000)
b.apply_force( (f_x,0.0),(0,0) )
ball = pm.Circle(b, r)
ball.elasticity = 0.75
ball.friction = 0.95
balls.append(ball)
space.add(ball,b)
print 'bowled'
ball_bowled += 1
def handle(self, balls):
if self.counter == FPS:
self.bowl(balls)
self.counter = 0
self.counter += 1
Collision handler
def ball_wall_collision(space, arb, balls, s_boxes):
shapes = arb.shapes
boxes = [box[0] for box in s_boxes] # Get walls
ball = None
wall = None
for ba in balls:
if ba in shapes:
ball = ba
break
for box in boxes:
if box in shapes:
wall = box
break
if wall and ball:
print 'removing'
space.remove(ball, ball.body) # Where the runtime problem happens
balls.remove(ball)
print 'removed'
return False
else:
return True
space.add_collision_handler(0,0,begin=ball_wall_collision,
balls=balls,s_boxes=s_boxes) # Other args to function
What am I doing wrong in the collision handling??
Am I missing something in the call to space.remove?
Is the function not working as I want it to??
Or is the error elsewhere (which I don't think it is)...
It looks like the problem is that you try to remove objects from the space in a collision handler during the simulation step.
Instead you can try with either manually collect all the balls into a list and then call remove after the step, or queue up removes with the post step callback like this:
space.add_post_step_callback(space.remove, ball)
space.add_post_step_callback(space.remove, ball.body)
(untested code)
I should probably try and make this more obvious in the API docs.. I wonder if it would be a good idea to automatically schedule the remove until end of step, or the less intrusive option, trigger a assert in python so you dont get the c++ error.