Move Car Horizontal - Python classes - python

I need to create a class for a car that moves on a horizontal line. The constructor must only have one argument and this is where it is throwing me off. I can only have one argument to initialize the initial position of the bug. It should default to a value of 0 and the initial direction should always be one. But i'm not sure i can do this without 2 arguments in the constructor. I also need two mutator methods for moving and turning the car as well as a accessor method that will display the location of the car.
Example: position 5 direction right: .....>; position 2 direction left: ..<
class Bug:
def __init__(self, iPosition=0):
self.position = iPosition
def move(self):
pos = self.postion
def turn(self):
direction = self.position
def display(self):
if direction < 0:
x = '<'
elif direction > 0:
x = '>'
for i in range(pos):
y = '.' + y
return (y,x)

Your code in the display() function should give you the answer. It has the movement being to the left for negative direction. Hence, a negative value in the constructor's parameter could represent movement to the left.

This might be more like what you need:
class Bug:
def __init__(self, iPosition=0):
self.position = iPosition
self.direction = 1
def move(self, distance=1):
self.position += self.direction * distance
def turn(self):
self.direction = -self.direction
def display(self):
if self.direction < 0:
x = '<'
elif self.direction > 0:
x = '>'
y = ''
for i in range(self.position): # this is not right if position is negative; maybe abs()?
y = '.' + y
return (y,x)

Related

PYGame - Changing bullets bug

First of all, my point is making 3 different bullets with different stats and different colors. Shoot them with Space key, selecting them with 1,2 and 3 keys. I'm not putting whole code here but if you need more info feel free to ask.
LaserDict = {
"redLaser": (RED_LASER,4,1), #Bad Velocity #Bad Firerate
"greenLaser": (GREEN_LASER,10,3), #Excellent Velocity #Good Firerate
"blueLaser": (BLUE_LASER,5,2) #Medium Velocity #Good Firerate
}
class Lasers():
def __init__(self,x,y,laserIndex):
self.x = x
self.y = y
self.laserNames = ["redLaser","greenLaser","blueLaser"]
self.laserIndex = laserIndex
self.laserImg, self.laserVel, self.laserCooldown = LaserDict[self.laserNames[self.laserIndex]]
def draw(self,window):
window.blit(self.laserImg,(self.x,self.y))
def move(self,p):
self.y += p
def offScreen(self,height):
return not (self.y>0 and self.y<=height) #if not in screen return True
class Player():
fpsCooldown = 30
def __init__(self,x,y,health,laserIndex):
blah blah...
self.cooldownReady = 0
self.lasers = []
self.laserIndex = laserIndex
def cooldown(self):
if self.cooldownReady >= self.fpsCooldown:
self.cooldownReady = 0
elif self.cooldownReady > 0:
self.cooldownReady += Lasers(self.x,self.y,self.laserIndex).laserCooldown
def shoot(self):
if self.cooldownReady == 0:
laser = Lasers(self.x+int(self.getWidth()/8) ,self.y, self.laserIndex)
self.lasers.append(laser)
self.cooldownReady = 1
def moveLasers(self):
self.cooldown()
for laser in self.lasers:
p = Lasers(self.x,self.y,self.laserIndex).laserVel
laser.move(-p)
if laser.offScreen(height):
self.lasers.remove(laser)
def main():
blah blah...
if keys[pygame.K_SPACE]:
player.shoot()
if keys[pygame.K_1]:
player.laserIndex = 0
if keys[pygame.K_2]:
player.laserIndex = 1
if keys[pygame.K_3]:
player.laserIndex = 2
Code is working fine with one bug. When you change your laser type by
pressing 1,2 or 3 it changes all of your Laser's stats exist in
window. For example if you shoot "Red Lasers" 5 times and if they are
not off the screen yet, and if you change your laser type to "GreenLasers" all of your "Red Lasers" remains red but moving just like
Green ones.
In moveLasers you calculate how much to move each laser with the variable p. This value p is calculated by creating a new Lasers with the current player.laserIndex, meaning that all lasers will move this much. You're better off changing Lasers.move to:
def move(self):
self.y += self.laserVel
rather than re-finding p everytime, which is only the laserVel for the current mode anyway
Create an object for your bullet, with its own stats.
Here, when you modify the bullet stats on the player, it affects all the bullets because you update your bullets inside the Player.
You could also keep a track of every bullet inside the player but it is overwhelming.
In you Bullet Object, just add it a direction, a speed and a color. The update every frame, just check if it reaches the end of the screen to erase it (to not lose memory).
Was this helpful?
Lasers.move() changed to:
def move(self):
self.y -= self.laserVel
def moveEnemyLasers(self,p):
self.y += p
Player.moveLasers() changed to:
def moveLasers(self):
self.cooldown()
for laser in self.lasers:
laser.move()
if laser.offScreen(height):
self.lasers.remove(laser)

How is 'moveSpeed' used here without being defined anywhere?

I found this pygame project online and was curious how 'moveSpeed' is being used as a number? like below?
def move(self, moveSpeed):
self.x -= moveSpeed
class HurdleManager:
def __init__(self, scale, spawnRange):
self.img = transform.scale(image.load('homework.png'), (7 * scale, 15 * scale))
self.spawnRange = spawnRange
self.hurdleList = []
self.scale = scale
def update(self, doSpawn, moveSpeed):
if doSpawn:
self.spawn()
self.manage(moveSpeed)
def manage(self, moveSpeed):
hurdles2 = []
for hurdle in self.hurdleList:
hurdle.update(moveSpeed)
if hurdle.onScreen():
hurdles2.append(hurdle)
self.hurdleList = hurdles2
spawnTick = 0
def spawn(self):
if self.spawnTick >= self.spawnRange[1]:
newHurdle = HurdleClass(windowX, self.img, 7 * self.scale, 15 * self.scale)
self.hurdleList.append(newHurdle)
self.spawnTick = 0
elif self.spawnTick > self.spawnRange[0]:
if random.randint(0, self.spawnRange[1] - self.spawnRange[0]) == 0:
newHurdle = HurdleClass(windowX, self.img, 7 * self.scale, 15 * self.scale)
self.hurdleList.append(newHurdle)
self.spawnTick = 0
self.spawnTick += 1
hurdleManager = HurdleManager(3, (45, 90))
class HurdleClass:
def __init__(self, x, img, width, height):
self.x = x
self.img = img
self.width = width
self.height = height
self.y = ground - height
def update(self, moveSpeed):
self.move(moveSpeed)
self.show()
def move(self, moveSpeed):
self.x -= moveSpeed
def show(self):
window.blit(self.img, (self.x, self.y))
def onScreen(self):
if self.x + self.width > 0:
return True
else:
return False
def move(self, moveSpeed):
def move says "I'm creating a function definition here. When I've finished defining it, I'd like the function to be bound to the name "move" in the current scope. Also, the function can use bindings from the current scope."
(self, moveSpeed) says "To run this function, I need two arguments. The first one is called self and the second one is called moveSpeed."
An argument is also known as a parameter. Just like when being asked to cut a cake, you might ask "Which cake?" and "Which knife?". In most programming languages, you pass parameters (also known as arguments) in the order each is declared. For example, given:
def cut(cake, knife):
...
writing cut(a, b) would "call" cut with the caller's "a" bound to the called function's "cake" variable and with the caller's "b" bound to the called function's "knife" variable.
In Python, you can also give the names of arguments explicitly. For example, cut(cake=a, knife=b) would do the same thing. These are called "keyword arguments." You don't need to pass things in declaration order when you use keyword arguments. For example, cut(knife=b, cake=a) would do the same thing as well.

Python How to check if a target object is no longer the same instance or "valid"

OK in my 2d game, I have several 'cell' object instances (sprite objects) that have an empty self.target = [] attribute upon initialization.
They will then find a valid "plant" target object nearby and that will become the cells' "self.target."
The 'cell' will then navigate toward the plant to eventually collide with and eat it, and the plant is then set to ".kill()" and another is respawned at a random new coordinate. There are several plant instances (part of spr_plant_group) and several cells (belonging to spr_cell_group).
Each plant 'lives' for X game ticks (200 I think), and if not eaten by then, is .kill()-ed and a new one spawns at a random coordinate.
The problem is this: If a cell's target plant happens to despawn from age, or is eaten by something, the cell's self.target info is STILL pointing to the old "plant" object's data. This means the cell is chasing a phantom object, still apparently having a valid x and y coordinate.
Question: How do I tell the cell that it's target is dead, gone, invalid? If I can do this, I think it would fix the 'phantom' target object.
Pieces of relevant code:
class Agent(sprite.Sprite):
def __init__(self, sense_range, size, food, maxspeed):
sprite.Sprite.__init__(self)
self.x = randint(0,450)
self.y = randint(0,450)
self.ang = randint(0,359)
self.turn_rate = 2
self.dx = 0
self.dy = 0
self.speed = 1
self.maxspeed = maxspeed
self.food = int(food)
self.max_food = food*1.5
self.target = []
self.sense_range = sense_range
# Snip------This part below is supposed to find a new target, but it only
# works for the first one when the cell spawns, then I can't seem to
# get it to become 'empty' so that the "if self.target == []" can do its
# thing....
def seek_food(self):
if (self.target == []):
#find cell a target within "sense_range" distance (say 200 pixels)
dist = self.sense_range
targ = []
for t in spr_plant_group:
t_dist = abs(self.x - t.x)
if t_dist <= dist:
targ = t
dist = t_dist
self.target = targ
print ("Found target...",dist, self.target)
else:
#already have a target, so move cell toward target
dx = self.target.x - self.x
dy = self.target.y - self.y
rads = atan2(dy,dx)
rads %= 2*pi
degs = degrees(rads)
direction = degs - self.ang
if direction > 0:
self.ang = self.ang + self.turn_rate
elif direction < 0:
self.ang = self.ang - self.turn_rate
# Correct for angle being out of 0-360* range
if self.ang > 360:
self.ang -= 360
elif self.ang < 0:
self.ang += 360
#---This is just a piece of the Plant class for your reference
class Plant (sprite.Sprite):
def __init__(self):
sprite.Sprite.__init__(self)
self.x = randint(0,450)
self.y = randint(0,450)
self.age = 1 + randint(0,50)
For reference, below. Plants age increases until 200 ticks, then they're killed, and a new one respawns...
def update_plants():
for shrub in spr_plant_group:
shrub.age += 1
# Respawn a new plant in a different place
if shrub.age >= 200:
shrub.kill()
plant.append (Plant())
EDIT: Actually, it's a lot simpler. Pygame's Sprite class supports an alive method, so just do this:
def seek_food(self):
if (self.target == [] or not self.target.alive()):
# Find a new target that isn't dead...
else:
# Move to the (alive) target...
You can use some variant of the Observer design pattern. Extend your Plant class with an extra property which keeps track of any Cell instances that are targeting the plant. When a cell targets a plant, it will add itself to the plant's list. Before the plant dies, it will notify all of its cells.
New property in the Plant class:
class Plant (sprite.Sprite):
def __init__(self):
# ...
self.followers = []
# ...
Cells subscribe to the plant's list of followers:
def seek_food(self):
if (self.target == []):
# ...
self.target = targ
self.target.followers.append(self)
# ...
else:
# ...
The Plant class overrides its parent class' kill function so that it notifies its followers before dying:
(Here, the cell class is directly modifying each plant's target, but you could encapsulate the behaviour differently if you want)
def kill(self):
for follower in self.followers:
follower.target = []
self.followers = []
super().kill()

Python checking if object member value is in list

I have this class called Point:
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
I have a list with Point objects and I call a method to check if it's in the list like this:
def isInList(self, list, point):
for cell in list:
if(cell == point):
return True
return False
However, it never goes inside the if statement; so it always returns false. I know for a fact that a point that matches has to be in the list due to a visualization I have showing up in my program. What am I doing wrong?
I call these two methods to make a list of Point. I pass cells and directions like this (called multiple times with different points and directions):
point = Point(2, 4)
direction = Direction.UP
newList = self.setCells(point, direction)
def setCells(self, point, direction):
pointList = []
index = 0
done = False
while index < 20 and done == False:
newPoint = self.getNextCell(point, direction)
if(point not in pointList):
pointList.append(newPoint)
point = pointList[len(pointList)-1]
index += 1
else:
done = True
return pointList
def getNextCell(self, point, direction):
if(direction == Direction.UP):
return Point(point.x-1, point.y, Direction.UP)
elif(direction == Direction.DOWN):
return Point(point.x+1, point.y, Direction.DOWN)
elif(direction == Direction.LEFT):
return Point(point.x, point.y-1, Direction.LEFT)
elif(direction == Direction.RIGHT):
return Point(point.x, point.y+1, Direction.RIGHT)
Direction is an enum:
class Direction(Enum):
NONE = 1
UP = 2
DOWN = 3
LEFT = 4
RIGHT = 5
Unfortunately the code from the OP's question seems to contain functions that were meant to be bound to an object (setCells and getNextCell), but are exhibited without their class. Furthermore the call to the constructor of Point in getNextCells had an additional argument (the direction), which I removed. Therefore I am considering the following code:
import enum
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def setCells(point, direction):
pointList = []
index = 0
done = False
while index < 20 and done == False:
newPoint = getNextCell(point, direction)
if(point not in pointList):
pointList.append(newPoint)
point = pointList[len(pointList)-1]
index += 1
else:
done = True
return pointList
class Direction(enum.Enum):
NONE = 1
UP = 2
DOWN = 3
LEFT = 4
RIGHT = 5
def getNextCell(point, direction):
if(direction == Direction.UP):
return Point(point.x-1, point.y)
elif(direction == Direction.DOWN):
return Point(point.x+1, point.y)
elif(direction == Direction.LEFT):
return Point(point.x, point.y-1)
elif(direction == Direction.RIGHT):
return Point(point.x, point.y+1)
point = Point(2, 4)
direction = Direction.UP
newList = setCells(point, direction)
print(point in newList)
which prints False. To investigate if this is correct it is useful to add some pretty printing to the class Point:
def __repr__(self):
return "Point(%s,%s)" % (self.x,self.y)
It is now possible to easily investigate the contents of the list:
>>> print(newList)
[Point(1,4)]
>>> print(point)
Point(2,4)
Since newList contains only one point which has a different x-coordinate it is obvious that point in newList should be false.
To better understand why newList does not contain point it is useful to give a simplified version of setCells:
def setCellsSimplified(point,direction):
return [getNextCell(point, direction)]
Explanation: Initially pointList is empty, so point not in pointList will be true regardless of the value of point. Thus newPoint is added to pointList and point is now equal to newPoint. Therefore, when the while-body runs again, point is in pointList and the second execution is the last execution of the while-body.
Since getNextCell always returns a new Point that differs in one coordinate from the object passed to it point and getNextCell(point,dir) are never equal (regardless of the direction) and therefore point in setCells(point,dir) is always false (which can be seen easily by looking at the simplified version setCellsSimplified).
I'm confused my how you are invoking this. Your function takes in self, list, and point. If this is a method of an object, and you want to see if the current object is in the list I would try something like this instead...
def isInList(self, list):
return self in list
That should give you an output similar to this...
>>> list = [Point(2,2), Point(3,3), Point(4,4)]
>>> x = Point(4,4)
>>> x.isInList(list)
True
>>> x = Point(4,5)
>>> x.isInList(list)
False

Change the speed of a single unchanging sprite object in pygame

I am doing a pygame program from a book where a chef drops pizzas and you have to catch them with a pan. The chef and pan sprites are only created once, while the pizza sprites obviously keep getting created. I am trying to make it so that as the score gets higher, the chef starts to move around faster (only moves in the x). I think I am running into trouble with class attributes vs instance attributes. I have been unable to find a way to access the score from the chef class, even though I tried making score a global variable or even just assigning it to a dummy global variable (This would allow me to make changes to the chefs speed within the update method). Alternatively, I tried accessing the chefs dx within the pan class since that is where the score is. I have also been unable to access that, even with the getattr method. Would greatly appreciate any suggestions on this one. Here is the code for the pan and chef class. I have commented out parts of stuff that I tried and didn't work.
from livewires import games, color
import random
games.init(screen_width = 640, screen_height = 480, fps = 50)
class Pan(games.Sprite):
"""
A pan controlled by player to catch falling pizzas.
"""
image = games.load_image("pan.bmp")
def __init__(self):
""" Initialize Pan object and create Text object for score. """
super(Pan, self).__init__(image = Pan.image,
x = games.mouse.x,
bottom = games.screen.height)
self.score = games.Text(value = 0, size = 25, color = color.black,
top = 5, right = games.screen.width - 10)
games.screen.add(self.score)
def update(self):
""" Move to mouse x position. """
self.x = games.mouse.x
if self.left < 0:
self.left = 0
if self.right > games.screen.width:
self.right = games.screen.width
self.check_catch()
def check_catch(self):
""" Check if catch pizzas. """
for pizza in self.overlapping_sprites:
self.score.value += 10
#increase the speed of the pizza
# if self.score.value % 100 == 0:
# Pizza.speed += 0.1
# print(Pizza.speed)
#increase the speed of the chef
# if self.score.value % 100 == 0:
# print(Chef.dx)
# print(x)
# y = int(x)*2
# print(y)
self.score.right = games.screen.width - 10
pizza.handle_caught()
class Chef(games.Sprite):
"""
A chef which moves left and right, dropping pizzas.
"""
image = games.load_image("chef.bmp")
speed = 2
def __init__(self, y = 55, odds_change = 200):
""" Initialize the Chef object. """
super(Chef, self).__init__(image = Chef.image,
x = games.screen.width / 2,
y = y,
dx = Chef.speed)
self.odds_change = odds_change
self.time_til_drop = 0
def update(self):
#x = getattr(Pan,"score")
#print(x)
""" Determine if direction needs to be reversed. """
if self.left < 0 or self.right > games.screen.width:
self.dx = -self.dx
elif random.randrange(self.odds_change) == 0:
self.dx = -self.dx
self.check_drop()
def check_drop(self):
""" Decrease countdown or drop pizza and reset countdown. """
if self.time_til_drop > 0:
self.time_til_drop -= 1
else:
new_pizza = Pizza(x = self.x)
games.screen.add(new_pizza)
# set buffer to approx 30% of pizza height, regardless of pizza speed
self.time_til_drop = int(new_pizza.height * 1.3 / Pizza.speed) + 1
def main():
""" Play the game. """
wall_image = games.load_image("wall.jpg", transparent = False)
games.screen.background = wall_image
the_chef = Chef()
games.screen.add(the_chef)
the_pan = Pan()
games.screen.add(the_pan)
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
# start it up!
main()
As the chef speed is a class variable you need to add the class name to access it, as in: Chef.speed. Where is the pizza class? I don't know Livewire so can't explain why you can't access the score, but surely you can set it as a number somehow and use it that way?
Thanks for your time. I was able to solve it by creating an attribute flag within the Pan class to check the score and then accessing that attribute flag in the Chef's update method, to then be able to change dx. Chef.speed is only the initial chef speed so changing it doesn't update the chef's dx.

Categories

Resources