1

I've been searching for a solution but still can't get it to work. I don't know what's wrong. I want to move my object smoothly just like this:

https://youtu.be/vc1pJ8XdZa0?t=153

but mine is always "teleporting" when moving. Here's part of my code

#Player
playerImg = pygame.image.load('yin.png')
playerX = 0
playerY = 0
playerX_move = playerY_move = 0
playerMoveUnit = 5

def player(x,y):
    screen.blit(playerImg,(x,y))

#Game Loop
running =True
while running:
    screen.blit(background,(-100,-80))

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                playerX_move-=playerMoveUnit
            if event.key == pygame.K_RIGHT:
                playerX_move+=playerMoveUnit
            if event.key == pygame.K_DOWN:
                playerY_move+=playerMoveUnit
            if event.key == pygame.K_UP:
                playerY_move-=playerMoveUnit
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                playerX_move+=playerMoveUnit
            if event.key == pygame.K_RIGHT:
                playerX_move-=playerMoveUnit
            if event.key == pygame.K_DOWN:
                playerY_move-=playerMoveUnit
            if event.key == pygame.K_UP:
                playerY_move+=playerMoveUnit

    playerX+=playerX_move
    playerY+=playerY_move

    #Check boundary
    if playerX <=-10:
        playerX = -10
    elif playerX >=(length-70):
        playerX = length-70
    if playerY <= -20:
        playerY = -20
    elif playerY >=(width-105):
        playerY = width-105

    player(playerX,playerY)

    pygame.display.update()

Ted Klein Bergman
  • 9,146
  • 4
  • 29
  • 50
王韋翰
  • 25
  • 1
  • 8
  • 1
    `playerX_move-=playerMoveUnit` You need to either reset `playerX_move = 0` at the begining of the loop, or just `playerX_move = playerMoveUnit`. Same for rest. – 001 May 18 '20 at 15:21

4 Answers4

0

just reduce your "playerMoveUnit"

Niraj
  • 51
  • 4
0

Use pygame.time.Clock() and .tick() to control the flops per second. The framerate argument the .tick() will delay to keep the game running slower than the given ticks per second. For instance:

clock = pygame.time.Clock()
FPS = 60

running =True
while running:
    clock.tick(FPS)

    # [...]

Furthermore I recommend to use pygame.key.get_pressed() rather than the keyboard events for the movement of the player. pygame.key.get_pressed() returns a sequence of boolean values representing the state of every key.
Get and evaluate the states of the keys in every frame and move the player accordingly:

clock = pygame.time.Clock()
FPS = 60
running =True
while running:
    clock.tick(FPS)

    screen.blit(background,(-100,-80))

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        playerX -= playerMoveUnit
    if keys[pygame.K_RIGHT]:
        playerX += playerMoveUnit
    if keys[pygame.K_UP]:
        playerY -= playerMoveUnit
    if keys[pygame.K_DOWN]:
        playerY += playerMoveUnit

    #Check boundary
    if playerX <=-10:
        playerX = -10
    elif playerX >=(length-70):
        playerX = length-70
    if playerY <= -20:
        playerY = -20
    elif playerY >=(width-105):
        playerY = width-105

    player(playerX,playerY)

    pygame.display.update()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
0

I notice that you do not have a clock.tick(...) in your code. That is needed to limit the frame rate.

You need to put it into your main while loop.

Somewhere at the top of the code you want to set a constant that controls your framerate. You'll also need a clock to call tick() on People commonly use a frame rate of 60, so you would want something like this near the top of your code:

FPS = 60

clock = pygame.time.Clock()

Then at the end of your while loop you need a clock.tick(FPS). I tend to put it just before the screen update like this:

while running:
    ....
    clock.tick(FPS)
    pygame.display.update()

FPS stands for Frames Per Second. The tick() call remembers the time that you last called it and does not return until an amount of time equal to 1/FPS has passed since the previous call. Putting it after the computation and just before the update() in the while loop ensures the screen updates occur as close as possible to those even 1/FPS time periods. You can find the docs here.

If your game logic per pass does not take very much time, without this limit your game will run very fast (as you discovered). A different issue but just as bad is, if the amount of computing taken each frame varies, then without imposing a constant frame rate the animation would not be smooth since each frame would display for different amounts of time.

Something like this was likely in the tutorial you were working with, but you may have missed it or not realized what it was for and left it out.

Edit:

After you said you had tried this and it still was not working, I looked at it a bit more. There are two other issues I see, though one may be intentional, I am not sure.

You have playerX_move, playerY_move and playerMoveUnit which together appear to be intended to control your speed. It appears that playerMoveUnit is supposed to be the absolute amount and that playerX_move and playerY_move are intended to be that applied to the X and Y directions. That would mean that you would assign playerMoveUnit to either playerX_move or playerY_move depending on the key press, like this:

if event.type == pygame.KEYDOWN:
    if event.key == pygame.K_LEFT:
        playerX_move = playerMoveUnit

However you are using += or -= instead. This means that each pass you will be increasing your speed by playerMoveUnit. That makes playerMoveUnit an acceleration not a speed and will result in your object moving very fast very quickly. This is likely the cause of the teleporting behaviour, it is just moving VERY fast.

The other issue I want to point out is that on the KEYDOWN you start the object moving in the desired direction, and on KEYUP you don't stop it by setting the speed to 0. Instead you start the speed going the other way. That is likely not the behaviour that you want. Your code will cause it to move one way when the key is pressed and then start moving in reverse when it is released and not stop till it hits the edge or another key is pressed. You likely want something like this instead:

if event.type == pygame.KEYUP:
    if event.key == pygame.K_LEFT:
        playerX_move = 0

By the way this would be cleaner if you structured the as a class so you could group all the player related information together.

I just noticed that @JohnnyMopp had a comment on your question that pointed out the += -= thing. Though I had not noticed it, he added that before I edited my answer to include that fix.

Glenn Mackintosh
  • 2,765
  • 1
  • 10
  • 18
  • I've tried this solution, but it doesn't work. I thought the tick() is just limit your fps? anyway, my object is still moving like teleporting... I've been searching "pygame move smooth" in Youtube, but the tutorial still can't fix my problem , don't know why... – 王韋翰 May 18 '20 at 15:56
  • also I have tried just fill my screen with pure black , not image, still doesn't work, and if I decrease my move per step(which Is playerMoveUnit) to 1 , it seems to be smooth, but it moves very very slow . In the vedio link I post , the object moves fast and smooth – 王韋翰 May 18 '20 at 15:59
  • Yes it is to limit the frame rate, but if the frame rate is very fast the visual result cab be that of the object moving extremely fast to where they are going and giving the impression of teleporting, though they are really just moving too fast for you to tell they covered the space in between. – Glenn Mackintosh May 18 '20 at 16:00
  • ye, I just tried it again, still teleporting... don't know why. getting crazyT_T.. I've been working on this for 6 hours. – 王韋翰 May 18 '20 at 16:05
  • im thinking if it's because Im using laptop, so it runs very slow – 王韋翰 May 18 '20 at 16:06
  • I tried print something in each while loop , it prints like 10 times a second, I think it's the problem? the loop runs too slow – 王韋翰 May 18 '20 at 16:20
  • @王韋翰 I have edited the answer at the end to include what I believe is the root of the problem, though you should do the FPS stuff as well. – Glenn Mackintosh May 18 '20 at 17:16
  • I've changed my moving method after @Rabbid76 told me. still doesn't work T_T. I think it's my laptop's issue. I just copy the code in the tutorial, and it is still teleporting – 王韋翰 May 18 '20 at 17:38
  • @王韋翰 when you said it was teleporting, I was assuming that you meant it was jumping straight to the place where it ended up. I am now starting to think that is not what you mean. By teleporting, do you mean that it is jumping from point to point as it travels across the screen instead of moving smoothly across it? If so that can be a couple of things. One is that your speed is too large and so each frame it moves too much. The other could be that you only have a single image. A single image will give the impression of gliding rather than walking. Can you better describe what the player does? – Glenn Mackintosh May 18 '20 at 18:35
  • A slow laptop doesn't seem the likely cause as it would just cause everything to slow down as if it was playing in slow motion, – Glenn Mackintosh May 18 '20 at 18:38
  • It was like , if the moving step is "5" , when I pressed RIGHT , it just suddenly jumped to next position. so I think it's bc single message? but Im not really understand how to solve it? – 王韋翰 May 19 '20 at 05:06
  • But some of the tutorial just use single image and still moves fast and smoothly. I just copy and paste some of the tutorial code , mine is still teleporting – 王韋翰 May 19 '20 at 05:14
0

You are not using sprites, but single image. You need to load multiple images in order to get smooth movement, in this case for player, if you are following this tutorial, you need 9 images for "left" and 9 for "right" movement. Loading can be done e.g. this way:

go_right = []
go_left = []
for i in range(1,10):
    sprite_r = pg.image.load("R{}.png".format(i))
    sprite_l = pg.image.load("L{}.png".format(i))
    go_right.append(sprite_r)
    go_left.append(sprite_l)

Then, in Player class define methods for left and right: def go_left(self):

    self.left = True
    self.right = False
    self.x -= self.v

def go_right(self):

    self.right = True
    self.left = False
    self.x += self.v

Now, in redraw() or whatever you called it, set FPS to 27 for example(for divisibility reason):

clock.tick(27)
if player.right:
        window.blit(go_right[player.walk_count//3], (player.x, player.y))
        player.walk_count += 1

    elif player.left:
        window.blit(go_left[player.walk_count//3], (player.x, player.y))
        player.walk_count += 1

    else:
        player.stand()
        window.blit(char, (player.x, player.y))

This if's are responsible for displaying appropriate images from the list (loaded at the beginning)

And at the end, in the main_loop you need to handle the events (left and right arrows e.g.):

if keys[pg.K_RIGHT]:
    if player.x < win_width - get_player_width: # or constant number
        player.go_right()

elif keys[pg.K_LEFT]:       
    if player.x > 0:
        player.go_left()

You will probably need to adjust some of the code but I made it following the given tutorial. I hope this will work for you

EDIT:

After additional info from the comments I managed to run your code, as others already suggested putting clock.tick(FPS) in main_loop solves the issue. Do not forget clock = pygame.time.Clock() line before the loop. Additionally, if you want smoother and animated movement apply the code I provided above

kaktus_car
  • 986
  • 2
  • 11
  • 19
  • oh! never seen this b4. So the "sprite" is in the part of pygame module? – 王韋翰 May 18 '20 at 16:30
  • but in this vedio (51:05) he didn't use sprite, still moves smoothly and fast. why? https://www.youtube.com/watch?v=FfWpgLFMI7w&t=6739s – 王韋翰 May 18 '20 at 16:37
  • I was watching this tutorial, and wondering why my object moves like teleporting , but his doesnt – 王韋翰 May 18 '20 at 16:37
  • Yes it is, https://www.pygame.org/docs/ref/sprite.html although `pygame.sprite` is not use here. It was done loading images into a list and then loading specific image from the list based on the `walk_count` – kaktus_car May 18 '20 at 16:39
  • Could you place the whole code so I can test it, I am now not sure what do you mean by "like teleporting" – kaktus_car May 18 '20 at 16:40
  • but the code I post is almost the whole code , just need to setup screen and init, etc. – 王韋翰 May 18 '20 at 16:58
  • what's the difference between sprite and single images? can't understand. I load my image and assigned to playerImg. isn't it alr loaded? – 王韋翰 May 18 '20 at 17:02
  • I think it's laptop's problem.... I just copy and paste the code from other tutorial using sprite , but still moving like teleporting. – 王韋翰 May 18 '20 at 17:39
  • I don't think it is, I've managed to run your code with info provided, see the edit – kaktus_car May 18 '20 at 17:41
  • I just don't understand why many tutorial's object can move so fast and smoothly by just using the very basic code. – 王韋翰 May 19 '20 at 10:52
  • Use pasteubuntu for example to copy your code and share link. When I see full code I will tell more – kaktus_car May 19 '20 at 11:03
  • OK I create a simple code https://paste.ubuntu.com/p/73qbH4rDKR/ – 王韋翰 May 19 '20 at 15:54
  • This code works as smoothly on my machine. What is your python/pygame version and computer specs – kaktus_car May 19 '20 at 23:08