对我的精灵不等的 +/- 速度感到困惑 [英] Puzzled by my sprite's unequal +/ - velocity

查看:22
本文介绍了对我的精灵不等的 +/- 速度感到困惑的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的游戏中有两个精灵.僵尸精灵完美运行,以 1.0 的速度向各个方向移动.然而,尽管所有方向上的所有值都是 2.25,但我的玩家精灵在正 x/y 方向移动得更慢.

I have two sprites in my game. The zombie sprite works perfectly, moving in all directions at a velocity of 1.0. My player sprite however, despite moves more slowly in the positive x/y direction, despite all values in all directions being 2.25.

就我的一生而言,我似乎看不出这里有什么问题.

For the life of me I can't seem to see what is wrong here.

完整的工作代码:

import pygame
import random
import sys
import itertools
import math
import time
from datetime import datetime
from librarymodified import *
from pygame.locals import *

# prints text using the supplied font
def print_text(font, x, y, text, color=(255,255,255)):
    imgText = font.render(text, True, color)
    DISPLAYSURF.blit(imgText, (x,y))

# MySprite class extends pygame.sprite.Sprite
class MySprite(pygame.sprite.Sprite):

    def __init__(self, target):
        pygame.sprite.Sprite.__init__(self) #extend the base Sprite class
        self.master_image = None
        self.frame = 0
        self.old_frame = -1
        self.frame_width = 1
        self.frame_height = 1
        self.first_frame = 0
        self.last_frame = 0
        self.columns = 1
        self.last_time = 0
        self.direction = 0
        self.times_hit = 0
        self.direction = 0
        self.velocity = Point(0.0,0.0)

    # times_hit property
    def _get_times_hit(self): return self.times_hit
    def _set_times_hit(self, hits): self.times_hit += hits
    number_hits_taken = property(_get_times_hit, _set_times_hit)

    #X property
    def _getx(self): return self.rect.x
    def _setx(self,value): self.rect.x = value
    X = property(_getx,_setx)

    #Y property
    def _gety(self): return self.rect.y
    def _sety(self,value): self.rect.y = value
    Y = property(_gety,_sety)

    #position property
    def _getpos(self): return self.rect.topleft
    def _setpos(self,pos): self.rect.topleft = pos
    position = property(_getpos,_setpos)


    def load(self, filename, width, height, columns):
        self.master_image = pygame.image.load(filename).convert_alpha()
        self.frame_width = width
        self.frame_height = height
        self.rect = Rect(0,0,width,height)
        self.columns = columns
        #try to auto-calculate total frames
        rect = self.master_image.get_rect()
        self.last_frame = (rect.width // width) * (rect.height // height) - 1

    def update(self, current_time, rate=30):
        #update animation frame number
        if current_time > self.last_time + rate:
            self.frame += 1
            if self.frame > self.last_frame:
                self.frame = self.first_frame
            self.last_time = current_time

        #build current frame only if it changed
        if self.frame != self.old_frame:
            frame_x = (self.frame % self.columns) * self.frame_width
            frame_y = (self.frame // self.columns) * self.frame_height
            rect = Rect(frame_x, frame_y, self.frame_width, self.frame_height)
            self.image = self.master_image.subsurface(rect)
            self.old_frame = self.frame

    def __str__(self):
        return str(self.frame) + "," + str(self.first_frame) + \
               "," + str(self.last_frame) + "," + str(self.frame_width) + \
               "," + str(self.frame_height) + "," + str(self.columns) + \
               "," + str(self.rect)

#Point class
class Point(object):
    def __init__(self, x, y):
        self.__x = x
        self.__y = y

    #X property
    def getx(self): return self.__x
    def setx(self, x): self.__x = x
    x = property(getx, setx)

    #Y property
    def gety(self): return self.__y
    def sety(self, y): self.__y = y
    y = property(gety, sety)

    def __str__(self):
        return "{X:" + "{:.0f}".format(self.__x) + \
            ",Y:" + "{:.0f}".format(self.__y) + "}"


def calc_velocity(direction, vel = 1.0):
    velocity = Point(0, 0)
    if direction == 0: # North
        velocity.y = -vel
    elif direction == 2: # East
        velocity.x = vel
    elif direction == 4: # south
        velocity.y = vel
    elif direction == 6: # west
        velocity.x = -vel
    return velocity

def reverse_direction(sprite):
    if sprite.direction == 0:
        sprite.direction = 4
    elif sprite.direction == 2:
        sprite.direction = 6
    elif sprite.direction == 4:
        sprite.direction = 0
    elif sprite.direction == 6:
        sprite.direction = 2




# main
pygame.init()

DISPLAYSURF = pygame.display.set_mode((800,600))
pygame.display.set_caption("Collision Detection")
font = pygame.font.SysFont(None, 36)
fpsclock = pygame.time.Clock()
fps = 30

# create sprite groups
zombie_group = pygame.sprite.Group()
player_group = pygame.sprite.Group()
health_group = pygame.sprite.Group()

# create player sprite
player = MySprite(DISPLAYSURF)
player.load("farmer walk.png", 96, 96, 8)
player.position = (80,80)
player.direction = 4
player_group.add(player)

# create zombie sprite
zombie_image = pygame.image.load("zombie walk.png").convert_alpha()
for i in range(1):
    zombie = MySprite(DISPLAYSURF)
    zombie.load("zombie walk.png", 96, 96, 8)
    zombie.position = (random.randint(0, 700), random.randint(0, 500))
    zombie.direction = random.randint(0,3) * 2
    zombie_group.add(zombie)

# create health sprite
health = MySprite(DISPLAYSURF)
health.load("health.png", 32, 32, 1)
health.position = (400, 300)
health_group.add(health)

game_over = False
player_moving = False
player_health = 100

# colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 0)
YELLOW = (255, 255, 0)

##DISPLAYSURF.fill(BLACK)
##pygame.mouse.set_visible(True)


# event loop
while True:
    ticks = pygame.time.get_ticks() # ms since pygame.init() called
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == MOUSEMOTION:
            mousex, mousey = event.pos

    # keyboard polling
    keys = pygame.key.get_pressed()
    if keys[K_ESCAPE]:
        pygame.quit()
        sys.exit()
    elif keys[K_UP] or keys[K_w]:
        player.direction = 0
        player_moving = True
    elif keys[K_RIGHT] or keys[K_d]:
        player.direction = 2
        player_moving = True
    elif keys[K_LEFT] or keys[K_a]:
        player.direction = 6
        player_moving = True
    elif keys[K_DOWN] or keys[K_s]:
        player.direction = 4
        player_moving = True
    else:
        player_moving = False

    # these things should not happen if game is over
    if not game_over:
        # update player sprite
        player_group.update(ticks, 50)
        # use player direction to calculate frame range
        player.first_frame = player.direction * player.columns
        player.last_frame = player.first_frame + player.columns-1
        if player.frame < player.first_frame:
            player.frame = player.first_frame

        if not player_moving:
            # stop animating when player is not moving
            player.frame = player.first_frame = player.last_frame
        else:
            # move player in that direction
            player.velocity = calc_velocity(player.direction, 1.5)
            player.velocity.x *= 1.5
            player.velocity.y *= 1.5

        # manually move player
        if player_moving:
            player.X += player.velocity.x
            player.Y += player.velocity.y
            if player.X <0: player.X = 0
            elif player.X > 700: player.X = 700
            if player.Y <0: player.Y = 0
            elif player.Y > 500: player.Y = 500


        # update zombie sprites
        zombie_group.update(ticks, 50)

        # manually update zombies
        for z in zombie_group:
            # set zombie animation range
            z.first_frame = z.direction * z.columns
            z.last_frame = z.first_frame + z.columns-1
            if z.frame < z.first_frame:
                z.frame = z.first_frame
            z.velocity = calc_velocity(z.direction)

            # keep zombie on screen
            z.X += z.velocity.x
            z.Y += z.velocity.y
            if z.X < 0 or z.X > 700 or z.Y < 0 or z.Y > 500:
                reverse_direction(z)

        # check for sprite collision
        attacker = 0
        attacker = pygame.sprite.spritecollideany(player, zombie_group)
        if attacker != None:
            # more precise check
            if pygame.sprite.collide_rect_ratio(0.5)(player, attacker):
                player_health -= 10
                if attacker.X < player.X: attacker.X -= 10
                elif attacker.X > player.X: attacker.X += 10
            else:
                attacker = None

        # update health drop
        health_group.update(ticks, 50)

        # check for collision with health
        if pygame.sprite.collide_rect_ratio(0.5)(player, health):
            player_health += 30
            if player_health >100: player_health = 100
            health.X = random.randint(0, 700)
            health.Y = random.randint(0, 500)

        # is player dead?
        if player_health <= 0:
            game_over = True

        # clear screen
        DISPLAYSURF.fill((50,50,100))

        # draw sprites
        player_group.draw(DISPLAYSURF)
        zombie_group.draw(DISPLAYSURF)
        health_group.draw(DISPLAYSURF)

        # draw energy bar
        pygame.draw.rect(DISPLAYSURF, WHITE, (299, 555, 203, 31), 2)
        pygame.draw.rect(DISPLAYSURF, GREEN, (301, 557, player_health * 2, 28))

        # print zombie and player velocities for purpose of testing
        print_text(font, 350, 460, "Zombie X vel: " +\
                   str(zombie.velocity.x) + "\nY vel: " +\
                   str(zombie.velocity.y))
        print_text(font, 350, 500, "Player X vel: " +\
                   str(player.velocity.x) + "\nY vel: " +\
                   str(player.velocity.y))

    if game_over:
        print_text(font, 300, 200, "G A M E   O V E R")

    pygame.display.update()
    fpsclock.tick(fps)

推荐答案

问题

精灵组正在使用精灵的 rect 属性绘制您的精灵.pygame Rect 对象只能容纳整数,因此它会截断所有浮点数.

Problem

The sprite group is drawing your sprite using the sprite's rect attribute. A pygame Rect object can only hold integers, so it'll truncate all floating point numbers.

假设您有一个 x = 5.

  • 如果添加1.1:x += 1.1 <=> x = x + 1.1 <=> x= 5 + 1.1 <=> x = 6.1 将被截断为 x = 6.它增加了 1.
  • 如果你减去1.1:x -= 1.1 <=> x = x - 1.1 <=> x= 5 - 1.1 <=> x = 3.9 将被截断为 x = 3.减少了 2.
  • If you add 1.1: x += 1.1 <=> x = x + 1.1 <=> x = 5 + 1.1 <=> x = 6.1 which will be truncated to x = 6. It have increased by 1.
  • If you subtract 1.1: x -= 1.1 <=> x = x - 1.1 <=> x = 5 - 1.1 <=> x = 3.9 which will be truncated to x = 3. It have decreased by 2.

换句话说:你向左移动的速度会比向右移动得更快(同样的原则适用于负数).这是一个演示它的示例:

In other words: You'll move faster in the left direction than the right (the same principle applies to negative numbers). Here's an example demonstrating it:

import pygame
pygame.init()


class Player(pygame.sprite.Sprite):
    def __init__(self, group):
        super(Player, self).__init__(group)
        self.image = pygame.Surface((32, 32))
        self.rect = self.image.get_rect()


screen = pygame.display.set_mode((100, 100))
group = pygame.sprite.Group()
player = Player(group)
clock = pygame.time.Clock()

while True:
    clock.tick(10)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            quit()
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                x = player.rect.x + 1.1
                print("Actual x:", x)
                player.rect.x = player.rect.x + 1.1
                print("Truncated x:", player.rect.x)
            elif event.key == pygame.K_LEFT:
                x = player.rect.x - 1.1
                print("Actual x:", x)
                player.rect.x = player.rect.x - 1.1
                print("Truncated x:", player.rect.x)

    screen.fill((255, 255, 255))
    group.draw(screen)
    pygame.display.update()

解决方案

使用浮点数作为位置很棒;它可以让精灵每帧移动少于一个像素(如果您的游戏每秒更新 120 次,而您希望精灵每秒仅移动 30 个像素).

Solution

Using floating point numbers for position is great; it makes it possible to move a sprite less than a pixel every frame (if your game updates 120 times per second and you want your sprite to move only 30 pixels per second).

然而,你必须弥补 rect 对象无法容纳它们的事实.最直接的解决方案是拥有一个属性 position ,它以浮点精度跟踪精灵的位置.然后在每次更新时将 rect 更改为属性的位置.像这样:

However, you have to compensate for the fact that the rect objects cannot hold them. The most straightforward solution is to have an attribute position which keep track of the position of the sprite in floating point precision. Then at every update change the rect to the position of the attribute. Like this:

import pygame
pygame.init()


class Player(pygame.sprite.Sprite):
    def __init__(self, group):
        super(Player, self).__init__(group)
        self.image = pygame.Surface((32, 32))
        self.rect = self.image.get_rect()
        self.position = self.rect.x  # Or whatever point of the rect you want the position to be.


screen = pygame.display.set_mode((100, 100))
group = pygame.sprite.Group()
player = Player(group)
clock = pygame.time.Clock()

while True:
    clock.tick(10)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            quit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                player.position += 1.1
                player.rect.x = player.position
            elif event.key == pygame.K_LEFT:
                player.position -= 1.1
                player.rect.x = player.position

    screen.fill((255, 255, 255))
    group.draw(screen)
    pygame.display.update()

我只展示了这种运动在 x 轴上的工作原理,但它在 y 轴上完全相同.

I've only showed how this movement works in the x-axis, but it's exactly the same on the y-axis.

这篇关于对我的精灵不等的 +/- 速度感到困惑的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆