I am currently using pygame and I have made an enemy and a player. I want the enemy to be able to move straight toward the player. The best solution I could find was:
if playerX > enimeX:
enimeX_change = 0.1
else:
enimeX_change = -0.1
if playerY > enimeY:
enimeY_change = 0.1
else:
enimeY_change = -0.1
This does make the enemy go toward the player, though it goes until it has an even Y or X value, then goes straight, so it's a curvy path. I want it to be a straight line from the current position to the players' position. Can someone help? Note:I am fairly new to coding so please dumb it down for me ha-
anyway you have the code you need but let me make it a function for you
def enmy_follow_player(playerX, playerY, enemy_list):
"""
If you want multiple enemies to follow the player, you can use a loop like
this and check them all one by one.
"""
for enemy in enemy_list:
if playerX > enemy[0]:
enemy[0] -= 0.1
else:
enemy[0] += -0.1
if playerY > enemy[1]:
enemy[1] -= 0.1
else:
enemy[1] += 0.1
position_of_all_enemy =[[100,100],[100,100],[100,100]]#store all enemy position in a list
player_pos = [100,100]
while True:
follow = enmy_follow_player(player_pos[0],player_pos[1],position_of_all_enemy ) #you need to call this function continuously in a loop
Related
So I am trying to scan for every tile in a game ("scan" as stepping in a tile and saving its coordinates). If the player bumps against an object, then it must try to go to one tile down and keep walking. I can even know which face the player is facing.
I am using pydirectinput because pyautogui wasn't really working with my game.
This is my code so far:
def coordinates():
visited_coordinates = []
t_end = time.time() + 90
movements = []
while time.time() < t_end:
for message in generated_object(): # generated object returns a list with 4 elements
if [message[0], message[1]] in visited_coordinates:
if "left" in movements:
if "down" in movements:
if "right" in movements:
pydirectinput.press("up")
if message[3] != 4: # 4 means it is facing up
pydirectinput.press("up") # if it isn't facing up, it needs to be pressed twice
movements.append("up")
else:
pydirectinput.press("right")
if message[3] != 12: # 12 means it is facing right
pydirectinput.press("right") # same with facing right, and so on
movements.append("right")
else:
pydirectinput.press("down")
if message[3] != 0: # 0 means it is facing down
pydirectinput.press("down")
movements.append("down")
else:
pydirectinput.press("left")
if message[3] != 8: # 8 means it is facing left
pydirectinput.press("left")
movements.append("left")
continue
movements = []
if message[2] != 4: # this means it went beyond the border, and it must return
pydirectinput.press("right", presses=2)
visited_coordinates.append([message[0], message[1]])
return visited_coordinates
One (of the many) problems with this code is that it takes forever to perform an action (I am working with named pipes, so I have to read every received message). And in the same vein, this is classic spaghetti code, which we all despise.
How can I write this code to be more "readable" and more efficient?
And on the other hand, as soon as I go beyond that border, I can't really come back. I should make the player not to move left again, but I don't really know how since I've been resetting the movements list
Could put items in a dictionary, then loop through those entries.
directions = { 'up': 4, 'right': 12, 'down': 0, 'left':8 }
for direction, val in directions .items():
if direction in movements:
pydirectinput .press( direction )
if message[3] != val:
pydirectinput .press( direction )
movements .append( direction )
break ## you might want to break out of the loop here
This game is a space shooter and I'm having a weird problem where my lasers only sometimes hit the enemy, and other times pass right through.
for lasers in amtLasers:
if lasers.y < invaders.y:
if lasers.x > invaders.x-56 and lasers.x < invaders.x+28:
amtLasers.pop(amtLasers.index(lasers))
amtInvaders.pop(amtInvaders.index(invaders))
score += 20
I have classes for the lasers and invaders and amtLasers is a list that stores the onscreen lasers until they get deleted. I don't have it set to destroy the enemy on contact, so I was able to notice that if I continue shooting an enemy, the same enemy will get hit sometimes and not get hit other times. Why does this happen and how can I fix it?
Here's an edit:
The lasers only hit the most recently spawned enemy, and I think I know why
class invader(object):
def __init__(self):
self.x = randint(30, 1020)
self.y = 0
color = randint(1, 3)
if color == 1:
self.col = (225,0,0)
elif color == 2:
self.col = (0,225,0)
elif color == 3:
self.col = (0,115,255)
self.vel = randint(1, 2)
When a new enemy is spawned, invaders.x and invaders.y track that specific enemy and none of the other ones because this class has multiple enemies onscreen at once. How would I track each enemy separately?
You will need to test to see if any of the lasers hit any of the enemies. Use 2 nested loops to iterate the enemies and the lasers:
for lasers in amtLasers[:]:
for invaders in amtInvaders[:]:
if lasers.y < invaders.y:
if lasers.x > invaders.x-56 and lasers.x < invaders.x+28:
amtLasers.pop(amtLasers.index(lasers))
amtInvaders.pop(amtInvaders.index(invaders))
score += 20
Note that you need to iterate over shallow copies of the list. See How to remove items from a list while iterating?.
I recommend to use pygame.Rect objects and colliderect for the collision test:
laser_rect = pygame.Rect(laser.x, laser.y, laser_width, laser_height)
invader_rect = pygame.Rect(invaders.x, invaders.y, invaders_width, invaders_height)
if laser_rect .colliderect(invader_rect):
amtLasers.pop(amtLasers.index(lasers))
amtInvaders.pop(amtInvaders.index(invaders))
score += 20
See How do I detect collision in pygame? and How to detect collisions between two rectangular objects or images in pygame.
I am making my first game in pygame, but when I have generated my world and try to move my character his sprite is still left in the old position. I can't blit my background every frame either as its too large and grinds pygame's performance to a halt.
I tried bliting every frame but that also slowed pygame down as well. Then I tried to make it blit around the character so it covers the old pic of my character, but I couldn't get that to work either.
def MakeTerrain():
TempY = 0
for o in range(51):
for i in range(51):
TempX = 16 * i
if TempX >= 800:
TempY = 16 * o
TempX = 0
rnd = random.randrange(10)
if rnd <= 8:
tile = Tile("Grass",[TempX,TempY])
else:
tile = Tile("Stone",[TempX,TempY])
Tiles.append(tile)
tile.BlitTile(Display)
Function for making my terrain [a.k.a world/background]
class Player():
def __init__(self,MovementSpeed=1):
self.DirectionX = 0
self.DirectionY = 0
self.PlayerPos = [0,0]
self.MovementSpeed = MovementSpeed
self.PlayerSprite = pygame.image.load("Jeffrey.png")
def BlitPlayer(self,display):
display.blit(self.PlayerSprite,(self.PlayerPos[0],self.PlayerPos[1]))
Class of my player with my blit function
class Tile():
def __init__(self,sprite,Position):
self.TilePosition = Position
self.TileSprite = pygame.image.load(sprite+".png")
def BlitTile(self,display):
display.blit(self.TileSprite,(self.TilePosition[0],self.TilePosition[1]))
Tile class
http://prntscr.com/nrtnyx
This is what happens
I think the idea of only redrawing the tiles near the player can work, but it needs to be reworked slightly.
This will require changing your position values to pygame.math.Vector2, so do that first, I would also recommend changing all instances of these values to self.pos so that the loop I will show you is easier to make.
self.pos = pygame.math.Vector2(Position)
To ascess the x and y values, they are simply self.pos.x or self.pos.y
Within the drawing code, you can first change it so it iterates through every sprite:
for sprite in my_sprites:
pass
Inside this loop, you can check the distance from the sprite to the player using these new vectors:
deltaVec = player.pos - sprite.pos # Find delta [difference] between player position and sprite position
len = deltaVec.length() # Find the length between those two points through the distance formula
Then, at the end, check to see if the new distance is under a certain threshhold:
if len < 100: # 100 being the distance, change if needed
sprite.draw()
In the end, the new drawing code would look like this:
for sprite in my_sprites:
deltaVec = player.pos - sprite.pos
len = deltaVec.length()
if len < 100: # 100 being the distance, change if needed
sprite.draw()
This should only redraw the tiles nearest to the player and thats it without having to deal with managing each tile.
I'm trying to write a python program that will make a turtle either move forward, turn left and then move, or turn right and then move randomly without ever crossing over its own previously drawn lines. It must stay within the screen and clear and restart once it has done the most possible moves. This is as far as I got:
import turtle
positions=[]
while 'x':
count=132
for x in range (132):
count-=1
print(count)
move = randint(1,3)
if move == 1:
turtle.fd(50)
if move == 2:
turtle.rt(90)
turtle.fd(50)
if move == 3:
turtle.lt(90)
turtle.fd(50)
if turtle.xcor()>290:
turtle.rt(180)
turtle.fd(50)
if turtle.xcor()<-290:
turtle.rt(180)
turtle.fd(50)
if turtle.ycor()>240:
turtle.rt(180)
turtle.fd(50)
if turtle.ycor()<-240:
turtle.rt(180)
turtle.fd(50)
turtle.clear()
How can I make it remember its positions and not go over them? Many thanks!
Below is my attempt to solve your autonomous turtle problem. I chose a set() to track visited positions but also coerced the turtle onto a grid to make sure that only a limited set of points could be visited. I didn't like your approach of doing a 180 when you hit a wall as that just makes you retrace your path and fail -- instead my turtle tries to avoid hitting the walls.
I use an invisible turtle clone to "test the waters" to see if a move is good or bad. If there are no good moves, it gives up, resets, and starts again. You have to close the window to kill the program:
import turtle
from random import shuffle
WIDTH, HEIGHT = 600, 500
INCREMENT = 50
TURTLE_WIDTH = 20
X, Y = 0, 1
def dot_round(x, base=INCREMENT):
return int(base * round(float(x) / base))
turtle.setup(WIDTH, HEIGHT)
turtle.shape("turtle")
while True:
positions = set()
while True:
position = (dot_round(turtle.xcor()), dot_round(turtle.ycor())) # coerce position to grid
if position in positions:
break # collision with line
positions.add(position)
turtle.setposition(position) # coerce turtle back onto our grid
moves = list(range(3))
shuffle(moves)
clone = None
for move in moves:
clone = turtle.clone() # use an invisible clone to test the waters
clone.hideturtle()
clone.penup()
if move == 1:
clone.right(90)
elif move == 2:
clone.left(90)
clone.forward(INCREMENT)
position = (dot_round(clone.xcor()), dot_round(clone.ycor()))
if position[X] <= TURTLE_WIDTH//2 - WIDTH//2 or position[X] > WIDTH//2 - TURTLE_WIDTH//2:
continue # avoid collision with wall
if position[Y] <= TURTLE_WIDTH//2 - HEIGHT//2 or position[Y] > HEIGHT//2 - TURTLE_WIDTH//2:
continue # avoid collision with wall
if position not in positions:
break
else: # no break
break # accept the inevitable, there's no good move
turtle.setheading(clone.heading())
turtle.forward(INCREMENT)
turtle.reset()
# close the turtle window to get out of this program
This turtle only looks one move ahead -- he easily gets himself into jams he can't get himself out of.
This hopefully gives you some ideas how to design your own turtle automaton.
I am making a roguelike in Pygame. I am trying to make my character move forward with either the W or the UP key. When SHIFT is held down, movement should be twice as fast. If I hold down SHIFT and then press W, the program works as it should, however, if I am holding down W, and the character is already moving forward, and then I press SHIFT, the character stops. This happens only occasionally, but it needs to be fixed.
character_pos = [0,0]
move_speed = 1
mods = pygame.key.get_mods()
if mods & KMOD_SHIFT:
move_speed = 2
key = pygame.key.get_pressed()
if key[pygame.K_w] or key[pygame.K_UP]:
w_count += move_speed
if w_count == 20:
w_count = 0
if not is_wall(character_pos[0], character_pos[1]-1):
character_pos[1] -= 1
Any ideas?
If w_count starts at 0 and is incremented in steps of either 1 or 2, it will always pass 20 as it increases. But if it is incremented by a combination of 1s and 2s, it might go from 19 to 21. Could that be the problem? Try changing if w_count == 20 to if w_count >= 20.
Apologies if I have misunderstood your code, I'm not 100% clear on the purpose of the counter.