Big Fish
Big Fish

Reputation: 13

How to make moveable image rotate to follow mouse

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

Upvotes: 1

Views: 1658

Answers (2)

Babbage
Babbage

Reputation: 1

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 

Upvotes: 0

skrx
skrx

Reputation: 20438

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()

Upvotes: 1

Related Questions