How to make moveable image rotate to follow mouse - python

What the title says, I have an image that I can control with W,S,A,D. But I want to make the player image that is movable rotate to face the position of my mouse. Here's the relevent code:
import pygame
import random
import time
import math
import sys
pygame.init()
#The size of the game window
display_width = 1280
display_height = 800
#Colors available
black = (0, 0, 0) #colours defined by RGB,
white = (255, 255, 255)
red = (200, 0, 0)
green = (0, 150, 0)
bright_red = (255, 0, 0)
bright_green =(0, 255, 0)
#This code opens up the Game window
gameDisplay = pygame.display.set_mode((display_width, display_height))
pygame.display.set_caption("Blockslayer")
clock = pygame.time.Clock()
pygame.mouse.set_visible(True)
#player character info
slayerImg = pygame.image.load('squareslayer.png').convert_alpha()
slayerWidth = 84
slayerHeight = 84
#mouse info
mouse_c = pygame.image.load("crosshair.png ").convert_alpha()
def crosshair(mousex,mousey):
mousex, mousey = pygame.mouse.get_pos()
small_ch = pygame.transform.scale(mouse_c, (20, 20))
gameDisplay.blit(small_ch, (mousex, mousey,))
print(mousex,mousey)
#player character
def slayer(x,y,):
#small_slayer = pygame.transform.scale(slayerImg, (120, 80,))
pos = pygame.mouse.get_pos()
angle = 360 - math.atan2(pos[1] - 84, pos[0] - 84) * 180 / math.pi
rotimage = pygame.transform.rotate((slayerImg), angle,)
rect = rotimage.get_rect(center=(x, y))
gameDisplay.blit(rotimage, rect,)
pygame.display.update()
#Game Logic
def block_game_loop():
x = (display_width * 0.45)
y = (display_height * 0.8)
pos = pygame.mouse.get_pos()
angle = 360 - math.atan2(pos[1] + x - 84, pos[0] + y - 84) * 180 / math.pi
rotimage = pygame.transform.rotate((slayerImg), angle,)
mousex, mousey = pygame.mouse.get_pos()
#def blockguy(blockguyX, blockguyY, blockguyW, blockguyH, ):
#blockguyX = random.randrange(0, 785)
#blockguyY = random.randrange (0, 600)
#blockguyW = 166
#blockguyH = 110
#blockguy_speed = 5
#Event handler
exit_game = False
while not exit_game:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
pressed = pygame.key.get_pressed()
if pressed[pygame.K_s]: y += 7
if pressed[pygame.K_w]: y -= 7
if pressed[pygame.K_a]: x -= 7
if pressed[pygame.K_d]: x += 7
gameDisplay.fill(white)
slayer(x, y,)
#Boundaries
if x > display_width:
x = 1275
if x < 0:
x = 5
if y > display_height:
y = 795
if y < 0:
y = 5
crosshair(mousex,mousey)
#blockguy(blockguyX, blockguyY, blockguyW, blockguyH, )
pygame.display.update()
clock.tick(60)
block_game_loop()
pygame.quit()
quit()
Code is pretty janked together because I don't really know wtf im doing, but here's how the thing works so far
Youtube video: https://www.youtube.com/watch?v=zShWAm4pSx8&feature=youtu.be

Take a look at this rotate function (read the comments).
import math
import pygame
pygame.init()
gray = (30, 30, 30)
display_width, display_height = (1280, 800)
gameDisplay = pygame.display.set_mode((display_width, display_height))
clock = pygame.time.Clock()
slayerImg = pygame.Surface((104, 84), pygame.SRCALPHA)
pygame.draw.polygon(slayerImg, (0, 120, 250), [(1, 1), (103, 42), (1, 83)])
def rotate(x, y, mouse_pos, image):
# Calculate x and y distances to the mouse pos.
run, rise = (mouse_pos[0]-x, mouse_pos[1]-y)
# Pass the rise and run to atan2 (in this order)
# and convert the angle to degrees.
angle = math.degrees(math.atan2(rise, run))
# Rotate the image (use the negative angle).
rotimage = pygame.transform.rotate(image, -angle)
rect = rotimage.get_rect(center=(x, y))
return rotimage, rect
def block_game_loop():
x = display_width * 0.45
y = display_height * 0.8
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
pressed = pygame.key.get_pressed()
if pressed[pygame.K_s]: y += 7
if pressed[pygame.K_w]: y -= 7
if pressed[pygame.K_a]: x -= 7
if pressed[pygame.K_d]: x += 7
mousex, mousey = pygame.mouse.get_pos()
# Boundaries
if x > display_width:
x = 1275
if x < 0:
x = 5
if y > display_height:
y = 795
if y < 0:
y = 5
gameDisplay.fill(gray)
rotimage, rect = rotate(x, y, (mousex, mousey), slayerImg)
gameDisplay.blit(rotimage, rect)
pygame.display.update()
clock.tick(60)
block_game_loop()
pygame.quit()
Addendum: Here's an example that shows you how you can shoot rotated bullets. You need some basic trigonometry knowledge, because you need to calculate the x- and y-velocity with math.cos and math.sin. That will give you a unit vector (with length 1) that you need to scale to the desired speed. Now you need to put the velocity list together with a rect, an extra position list and the rotated image into a list that represents a bullet object. To update the rect position you first have to add the velocity to the pos and then assign the pos to the rect center (you have to do it that way, because rects can only have ints as the x and y position).
I recommend to use pygame.math.Vector2s and pygame sprites and sprite groups instead of lists as you can see in the linked answer, because that's a lot nicer to read. You still need to add code to remove bullets, which would also be simpler to implement with sprites and sprite groups.
import math
import pygame as pg
from pygame.math import Vector2
pg.init()
screen = pg.display.set_mode((640, 480))
FONT = pg.font.Font(None, 24)
BLACK = pg.Color('black')
BULLET_IMAGE = pg.Surface((20, 11), pg.SRCALPHA)
pg.draw.polygon(BULLET_IMAGE, pg.Color('grey11'), [(0, 0), (20, 5), (0, 11)])
def update_bullets(bullets):
"""Add the velocity to the pos then assign pos to the rect center."""
for bullet_rect, pos, velocity, _ in bullets:
pos[0] += velocity[0]
pos[1] += velocity[1]
bullet_rect.center = pos
def draw_bullets(bullets, screen):
for bullet_rect, pos, _, image in bullets:
screen.blit(image, bullet_rect)
pg.draw.rect(screen, (200, 140, 0), bullet_rect, 1)
def main():
clock = pg.time.Clock()
# The cannon image and rect.
cannon_img = pg.Surface((60, 22), pg.SRCALPHA)
pg.draw.rect(cannon_img, pg.Color('grey19'), [0, 0, 35, 22])
pg.draw.rect(cannon_img, pg.Color('grey19'), [35, 6, 35, 10])
orig_cannon_img = cannon_img # Store orig image to preserve quality.
cannon = cannon_img.get_rect(center=(320, 240))
angle = 0 # Angle of the cannon.
# Add bullets to this list. Bullets will also be lists
# consisting of a pygame.Rect, the velocity and the image.
bullets = []
bullet_speed = 5
playing = True
while playing:
for event in pg.event.get():
if event.type == pg.QUIT:
playing = False
elif event.type == pg.MOUSEBUTTONDOWN:
# Left button fires a bullet from cannon center with
# current angle. Add the bullet to the bullets list.
if event.button == 1:
# Use cosine and sine to calculate the x and y
# velocity. Scale them by the desired speed.
velocity = (math.cos(math.radians(angle)) * bullet_speed,
math.sin(math.radians(angle)) * bullet_speed)
img = pg.transform.rotate(BULLET_IMAGE, -angle)
bullet_rect = img.get_rect(center=cannon.center)
# The extra pos list is needed because the pygame.Rect
# can only have ints as the x and y value. We still
# need the rect for collision detection.
pos = list(bullet_rect.center)
bullet = [bullet_rect, pos, velocity, img]
bullets.append(bullet)
update_bullets(bullets)
# Find angle to target (mouse pos).
x, y = Vector2(pg.mouse.get_pos()) - cannon.center
angle = math.degrees(math.atan2(y, x))
# Rotate the cannon image.
cannon_img = pg.transform.rotate(orig_cannon_img, -angle)
cannon = cannon_img.get_rect(center=cannon.center)
# Draw
screen.fill(pg.Color('darkseagreen4'))
draw_bullets(bullets, screen)
screen.blit(cannon_img, cannon)
txt = FONT.render('angle {:.1f}'.format(angle), True, BLACK)
screen.blit(txt, (10, 10))
pg.draw.line(
screen, pg.Color(150, 60, 20),
cannon.center, pg.mouse.get_pos(), 2)
pg.display.update()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()

Here Big Fish, this is my code for that problem:
import math
sprite = sprite_new("spr_plasma_blast")
targetx = 0
targety = 0
m = 0
time = 0
speed = 8
if time == 0:
m = (targety - y)/(targetx - x)
xSpeed = math.sqrt((speed*speed)/((m*m) + 1))
if targetx < x:
xSpeed = -xSpeed
x = x + xSpeed
y = y + xSpeed*m
time = time + 1

Related

How to Move Sprite to a Specific Location Without Keys

Im trying to make a game with pygame where I can click a sprite then click somewhere on the screen for the sprite to move towards. So far, I'm able to click the sprite and get a response but I'm not sure how to tell the sprite to go to a given location where I click. I've seen something online with sprite.goal but I can't seem to make it work.
This is what I have
if event.type==pygame.MOUSEBUTTONDOWN:
pos=pygame.mouse.get_pos()
#White is a rectangle
if White.collidepoint(pos):
Moving=True
elif Moving==True:
#This is where I would tell it to move to pos
I'll show you a very simple example. Write a function that moves a point 1 step to a target point and returns the new position:
def move_to_target(pos, target):
x, y = pos
if x < target[0]:
x += 1
elif x > target[0]:
x -= 1
if y < target[1]:
y += 1
elif y > target[1]:
y -= 1
return (x, y)
Set a new target when the mouse button is pressed and call the function at each frame:
import pygame
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
def move_to_target(pos, target):
x, y = pos
if x < target[0]:
x += 1
elif x > target[0]:
x -= 1
if y < target[1]:
y += 1
elif y > target[1]:
y -= 1
return (x, y)
my_sprite = pygame.Surface((20, 20), pygame.SRCALPHA)
pygame.draw.circle(my_sprite, (255, 255, 0), (10, 10), 10)
pos = (200, 200)
target = (200, 200)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
target = event.pos
pos = move_to_target(pos, target)
window.fill(0)
window.blit(my_sprite, pos)
pygame.display.flip()
clock.tick(100)
pygame.quit()
exit()
For variable speed and a straighter and smoother movement, you need to tweak the function. See How to make smooth movement in pygame.
import pygame
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
LERP_FACTOR = 0.05
minimum_distance = 0
maximum_distance = 100
def move_to_target(pos, target):
target_vector = pygame.math.Vector2(*target)
follower_vector = pygame.math.Vector2(*pos)
new_follower_vector = pygame.math.Vector2(*pos)
distance = follower_vector.distance_to(target_vector)
if distance > minimum_distance:
direction_vector = (target_vector - follower_vector) / distance
min_step = max(0, distance - maximum_distance)
max_step = distance - minimum_distance
step_distance = min_step + (max_step - min_step) * LERP_FACTOR
new_follower_vector = follower_vector + direction_vector * step_distance
return (new_follower_vector.x, new_follower_vector.y)
my_sprite = pygame.Surface((20, 20), pygame.SRCALPHA)
pygame.draw.circle(my_sprite, (255, 255, 0), (10, 10), 10)
pos = (200, 200)
target = (200, 200)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
target = event.pos
pos = move_to_target(pos, target)
window.fill(0)
window.blit(my_sprite, pos)
pygame.display.flip()
clock.tick(100)
pygame.quit()
exit()

How to draw a circle onto a surface and rotate it while moving?

I am trying to:
draw a circle onto my window
let it move AND rotate at the same time to simulate a rolling ball.
Appreciate anyone's solutions! Attached below is my code.
import pygame
#create win
W, H = 700, 700
win = pygame.display.set_mode((W, H))
pygame.display.set_caption("Rotating Ball Simul")
#color lib
BG = (20,20,20)
BLUE = (0,0,255)
#draw circle
ballX, ballY = 0, 0
ballW = 20
ballH = ballW
ball = pygame.draw.circle(win, BLUE, (ballX, ballY), ballW, 1)
def redraw_window():
win.fill(BG)
win.blit(ball, (ballX, ballY))
pygame.display.update()
def main():
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
redraw_window()
if __name__ == '__main__':
main()
A circle always looks the same when you rotate it. You must use a sphere image to see the rotation. Read about How do I rotate an image around its center using PyGame? and move the ball image and change the rotation of the image depending on the movement. The angle of rotation depends on the movement and the diameter of the ball:
angle -= (self.x - prev_x) / self.diameter * 180 / math.pi
self.image = pygame.transform.rotate(self.original_image, self.angle)
self.rect = self.image.get_rect(center = self.rect.center)
Minimal example:
repl.it/#Rabbid76/PyGame-FollowInGrid
import os
import math
import pygame
class MarbelSprite(pygame.sprite.Sprite):
def __init__(self, x, ground, diameter, velocity, filename):
pygame.sprite.Sprite.__init__(self)
try:
self.image = pygame.transform.smoothscale(pygame.image.load(filename).convert_alpha(), (diameter, diameter))
except:
self.image = pygame.Surface((diameter, diameter), pygame.SRCALPHA)
pygame.draw.circle(self.image, (255, 128, 0), (diameter // 2, diameter // 2), diameter // 2)
self.original_image = self.image
self.rect = self.image.get_rect(midbottom = (x, ground))
self.diameter = diameter
self.x = x
self.velocity = velocity
self.move_x = 0
self.follow = None
self.angle = 0
def update(self, time, restriction):
move_x = 0
prev_x = self.x
if self.move_x != 0:
move_x = self.move_x * self.velocity * time
elif self.follow:
dx = self.follow.rect.centerx - self.x
move_x = (-1 if dx < 0 else 1) * min(self.velocity * time, abs(dx))
self.x += move_x
self.x = max(restriction.left + self.diameter // 2, min(restriction.right - self.diameter // 2, self.x))
self.rect.centerx = round(self.x)
self.angle -= (self.x - prev_x) / self.diameter * 180 / math.pi
self.image = pygame.transform.rotate(self.original_image, self.angle)
self.rect = self.image.get_rect(center = self.rect.center)
pygame.init()
window = pygame.display.set_mode((500, 300))
clock = pygame.time.Clock()
ground_level = 220
object = MarbelSprite(window.get_rect().centerx, ground_level, 100, 0.4, 'BaskteBall.png')
follower = MarbelSprite(window.get_width() // 4, ground_level, 50, 0.2, 'TennisBall.png')
all_sprites = pygame.sprite.Group([object, follower])
run = True
while run:
time = clock.tick(60)
for events in pygame.event.get():
if events.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
object.move_x = keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]
follower.follow = object
all_sprites.update(time, window.get_rect())
window.fill((32, 64, 224))
pygame.draw.rect(window, (80, 64, 64), (0, ground_level, window.get_width(), window.get_height()-ground_level))
all_sprites.draw(window)
pygame.display.flip()
pygame.quit()
exit()

PyGame Collisions does only work on one side of the rectangle [duplicate]

This question already has an answer here:
How to detect collisions between two rectangular objects or images in pygame
(1 answer)
Closed 2 years ago.
I'm just trying something with collisions and found the way to check one side of the rectangle with the other side.
I have the following problem:
If I move my game character (pink box) from the left against the object, my game character just moves through it:
If I come from the other side, everything works and my game character stops.
I mean to say that I need the same code for both sides but have to change the sides from if not player_rect.left == other_rect.right: to if not player_rect.right == other_rect.left:. But this does not work for one side.
import pygame
import sys
pygame.init()
clock = pygame.time.Clock()
window = pygame.display.set_mode([1200, 800])
pygame.display.set_caption("Collision Test")
x = 300
y = 300
width = 48
height = 96
velocity = 5
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
is_pressed = pygame.key.get_pressed()
player_rect = pygame.Rect(x, y, width, height)
other_rect = pygame.Rect(400, 300, 50, 50)
if is_pressed[pygame.K_d]:
if not player_rect.right == other_rect.left:
x += velocity
if is_pressed[pygame.K_a]:
if not player_rect.left == other_rect.right:
x -= velocity
window.fill((100, 150, 50))
pygame.draw.rect(window, (255, 50, 100), player_rect)
pygame.draw.rect(window, (255, 100, 50), other_rect)
pygame.display.update()
clock.tick(60)
Use collideRect().
Move the object and test if the rectangles are colliding. When a collision is detected, change the position of the object according to the moving direction:
is_pressed = pygame.key.get_pressed()
move_right = is_pressed[pygame.K_d]
move_left = is_pressed[pygame.K_a]
if move_right:
x += velocity
if move_left:
x -= velocity
player_rect = pygame.Rect(x, y, width, height)
other_rect = pygame.Rect(400, 300, 50, 50)
if player_rect.colliderect(other_rect):
if move_right:
player_rect.right = other_rect.left
x = player_rect.left
if move_left:
player_rect.left = other_rect.right
x = player_rect.left
For a smooth movement you've to do evaluate pygame.key.get_pressed() in the application loop rather than the event loop.
See also What all things happens inside pygame when I press a key? When to use pygame.event==KEYDOWN.
import pygame
import sys
pygame.init()
clock = pygame.time.Clock()
window = pygame.display.set_mode([1200, 800])
pygame.display.set_caption("Collision Test")
x = 300
y = 300
width = 48
height = 96
velocity = 5
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
is_pressed = pygame.key.get_pressed()
move_right = is_pressed[pygame.K_d]
move_left = is_pressed[pygame.K_a]
if move_right:
x += velocity
if move_left:
x -= velocity
player_rect = pygame.Rect(x, y, width, height)
other_rect = pygame.Rect(400, 300, 50, 50)
if player_rect.colliderect(other_rect):
if move_right:
player_rect.right = other_rect.left
x = player_rect.left
if move_left:
player_rect.left = other_rect.right
x = player_rect.left
window.fill((100, 150, 50))
pygame.draw.rect(window, (255, 50, 100), player_rect)
pygame.draw.rect(window, (255, 100, 50), other_rect)
pygame.display.update()
clock.tick(60)

Pygame - Python - 360-degree angle on a cube, cursor / pointer / mouse orientation

I am trying to solve this problem of the angle, I noticed that there are two 0,0 in the coordinates, maybe that is what is preventing the cube from making a 360 degree turn, follows the video and the code.
Can someone help me?
video here
import pygame
import sys
import os
import math
def main():
pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode([1000, 500])
pygame.display.set_caption('Example 1')
player = pygame.image.load(os.path.join('img', 'rect.png')).convert_alpha() #path to cube ./img/rect.png
pygame.font.init()
font = pygame.font.get_default_font()
font_angle = pygame.font.SysFont(font, 44, True)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
clock.tick(60)
screen.fill((255, 255, 255)) #screen background color
player_rect = player.get_rect() #player rect (for center position)
mouse_x, mouse_y = pygame.mouse.get_pos() #mouse position (x, y)
#to define angle - start
hypo = math.sqrt(math.pow(mouse_x - (player_rect[0] + player_rect.centerx), 2) +
math.pow(mouse_y - (player_rect[1] + player_rect.centery), 2))
cos = (mouse_x - (player_rect[0] + player_rect.centerx)) / hypo
sin = (mouse_y - (player_rect[1] + player_rect.centery)) / hypo
angle = (180 / math.pi) * - math.atan2(sin, cos)
#end
newplayer = pygame.transform.rotate(player, angle) #rotate cube
screen.blit(newplayer, [300, 100]) #show cube in screen
text = font_angle.render(str("%.2f" % angle), 1, (255, 0, 0)) #show angle in mouse position
screen.blit(text, ((mouse_x+20), mouse_y)) #show text
pygame.display.update() #update frames
main()
You can just subtract the player_rect.centerx position from the mouse_x position (the same for y) and pass it to math.atan2:
dist_x = mouse_x - player_rect.centerx
dist_y = mouse_y - player_rect.centery
angle = -math.degrees(math.atan2(dist_y, dist_x))
Create the rect before the while loop starts and pass the desired center coordinates. Use get_rect to create a new rect after the image is rotated and pass the center coords of the previous rect to keep it centered. And blit the image at the player_rect (that means at its topleft coordinates).
import pygame
import sys
import os
import math
def main():
pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode([1000, 500])
# player = pygame.image.load(os.path.join('img', 'rect.png')).convert_alpha() #path to cube ./img/rect.png
player = pygame.Surface((50, 30), pygame.SRCALPHA)
pygame.draw.polygon(player, (100, 200, 255), [(0, 0), (50, 15), (0, 30)])
player_rect = player.get_rect(center=(500, 250)) # player rect (for center position)
font = pygame.font.get_default_font()
font_angle = pygame.font.SysFont(font, 44, True)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
clock.tick(60)
screen.fill((255, 255, 255))
mouse_x, mouse_y = pygame.mouse.get_pos()
dist_x = mouse_x - player_rect.centerx
dist_y = mouse_y - player_rect.centery
angle = -math.degrees(math.atan2(dist_y, dist_x))
newplayer = pygame.transform.rotate(player, angle)
# Create a new rect and pass the center of the old rect.
player_rect = newplayer.get_rect(center=player_rect.center)
screen.blit(newplayer, player_rect) # Blit it at the player_rect.
text = font_angle.render(str("%.2f" % angle), 1, (255, 0, 0))
screen.blit(text, ((mouse_x+20), mouse_y))
pygame.display.update()
main()

Drawing line with infinity length in the direction of cursor in Pygame

I'm searching for some help with pygame. I'm developing simple game in Python to learn Pygame. I want to make spaceship which we can rotate and we can shooting with laser line.
I have done controlling by arrow keys, we can also rotating spaceship with mouse position, but I've got a problem with shooting. I want to make a line with infinity length from spaceship position to mouse direction. How I can do that? Here is my code:
def draw_objects(self):
SCREEN.fill(BLACK)
self.target = pygame.mouse.get_pos()
self.x = self.player.position[0] #player x position
self.y = self.player.position[1] #player y position
self.mx = self.target[0] #mouse x position
self.my = self.target[1] #mouse y position
self.slope=float(float(self.y-self.my)/float(self.x-self.mx+0.1)) #slope
self.x_new = DISPLAY_WIDTH #ray length
self.y_new = self.y + self.slope * (self.x_new - self.x)
self.player.draw()
self.draw_columns()
for agent in self.all_agents:
agent.draw()
agent.draw_vectors()
if self.player.shoot == True:
pygame.draw.line(SCREEN, GREEN, self.player.position,(self.x_new, self.y_new), 2)
pygame.display.update()
It's not working properly, because it's work only to the right of spaceship. In other cases it draws a line in reflection to cursor.
I will be grateful for your help.
slope doesn't keep direction.
You have to get sign of player_x - mouse_x + 0.1 and use with x_new
dx = player_x - mouse_x + 0.1
reversed_sign_x = 1 if dx < 0 else -1
x_new = reversed_sign_x * DISPLAY_WIDTH
Full working example:
move mouse to move line,
click left button to set player new position.
.
import pygame
# --- constants ---
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
DISPLAY_WIDTH = 800
DISPLAY_HEIGHT = 600
# --- functions ---
def calculate(player_x, player_y, mouse_x, mouse_y):
dx = player_x - mouse_x + 0.1
dy = player_y - mouse_y
reversed_sign_x = 1 if dx < 0 else -1
#reversed_sign_y = 1 if dy < 0 else -1
slope = dy/dx
x_new = reversed_sign_x * DISPLAY_WIDTH
y_new = player_y + slope * (x_new - player_x)
return x_new, y_new
# --- main ---
# - init -
pygame.init()
SCREEN = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
# - objects -
player_x = DISPLAY_WIDTH // 2
player_y = DISPLAY_HEIGHT // 2
mouse_x = player_x
mouse_y = player_y
x_new, y_new = calculate(player_x, player_y, mouse_x, mouse_y)
# - mainloop -
clock = pygame.time.Clock()
running = True
while running:
# - events -
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
player_x, player_y = event.pos
elif event.type == pygame.MOUSEMOTION:
x_new, y_new = calculate(player_x, player_y, event.pos[0], event.pos[1])
# - updates -
# empty
# - draws -
SCREEN.fill(BLACK)
pygame.draw.line(SCREEN, GREEN, (player_x, player_y), (x_new, y_new), 2)
pygame.display.flip()
# - FPS -
clock.tick(25)
# - end -
pygame.quit()
furas is right, you have to check whether the mouse is on the left or right side of the player and negate the DISPLAY_WIDTH if it's on the left. I came to a similar solution:
def target_coords(position):
x, y = position # Unpack player position into x, y.
mx, my = pygame.mouse.get_pos() # Unpack mouse pos into mx, my.
slope = (y-my) / (x-mx+0.1)
# This is a conditional expression, similar to `if condition: ... `else: ... `.
x_new = DISPLAY_WIDTH if x < mx else -DISPLAY_WIDTH
y_new = y + slope * (x_new - x)
return x_new, y_new
Note that this function only computes the target coordinates and returns them (functions should preferably do only one thing). Draw the line and everything else in another function.
There's also an alternative solution: You could use pygame vectors and first calculate the vector to the target, normalize it and scale it by the desired length (the DISPLAY_WIDTH).
import pygame
from pygame.math import Vector2
pygame.init()
DISPLAY_WIDTH = 640
GREEN = pygame.Color('aquamarine1')
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
position = Vector2(300, 200) # A pygame.math.Vector2 as the position.
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill((30, 30, 30))
pygame.draw.circle(screen, GREEN, (int(position.x), int(position.y)), 7)
# Calculate the vector to the target by subtracting pos from mouse pos.
# Normalizing it gives you a unit vector which you can scale
# by multiplying it with the DISPLAY_WIDTH.
target_vec = (pygame.mouse.get_pos()-position).normalize() * DISPLAY_WIDTH
pygame.draw.line(screen, GREEN, position, position+target_vec, 2)
pygame.display.flip()
clock.tick(30)
pygame.quit()

Categories