Pygame Как мне сделать наклоны?

#python #pygame

#python #pygame

Вопрос:

ВИДЕО У меня есть базовый проигрыватель, перемещающийся по экрану, и у меня есть отображаемое изображение, которое я хочу использовать для наклона, когда мой проигрыватель находится на изображении, он может перемещаться так, как изображение, но у меня возникают проблемы с этим, потому что я использую прямоугольник, и я не уверен, чтокак это будет работать. есть ли способ, которым я мог бы заставить своего игрока двигаться как изображение наклона, а не просто двигаться прямо

мой код прямо сейчас, я только что сделал это, когда игрок сталкивается с моими наклонами, чтобы двигаться вперед, но как я могу улучшить его, чтобы он выглядел так, как будто он движется с наклоном изображения??

 import pygame
pygame.init()


# make our screen for our game
window = pygame.display.set_mode((700,800))

# name our game
pygame.display.set_caption("Our Space Game")


class player:
    def __init__(self,x,y,height,width,color):
        self.x = x
        self.y = y
        self.height = height
        self.width = width
        self.color = color

        # make our player jump
        self.isJump = False
        self.JumpCount = 10

        self.fall = 0


        self.rect = pygame.Rect(x,y,height,width)

                
    def draw(self):
        self.rect.topleft = (self.x,self.y)
        pygame.draw.rect(window,self.color,self.rect)

white = (255,255,255)
playerman = player(200,200,10,10,white)







class slope():
    def __init__(self,x,y,height,width,color):
        self.x = x
        self.y = y
        self.height = height
        self.width = width
        self.color  = color
        self.image = pygame.image.load("hills1.png")
        self.rect = pygame.Rect(x,y,height,width)
    def draw(self):
        self.rect.topleft = (self.x,self.y)
        pygame.draw.rect(window,self.color,self.rect)
        window.blit(self.image,self.rect)




slopes = []

platformGroup = pygame.sprite.Group

level = [
"                                                                  ",
"                                                                  ",
"                                                                  ",
"                                                                  ",
"                                                                  ",
"                                                                  ",
"                                                                  ",
"   c                                                              "





]
                                                                                    
for iy, row in enumerate(level):
    for ix, col in enumerate(row):
        if col == "c":
            new_platforms = slope(ix*9.8, iy*50, 520,220,(23, 32, 42))
            slopes.append(new_platforms)
        
        


# our game fps
fps = 40
clock = pygame.time.Clock()


def redraw():
    window.fill((0,0,0))
    playerman.draw()

    for slope in slopes:
        slope.draw()


# our game main loop
runninggame = True
while runninggame:
    clock.tick(fps)
    for event in pygame.event.get():
        if event.type == pygame.quit:
            runninggame = False



    keys = pygame.key.get_pressed()
    for slope in slopes:
        if playerman.rect.colliderect(slope.rect):
            playerman.x  = 7


    collide = False
    if not playerman.isJump:                    
        playerman.y  = playerman.fall
        playerman.fall  = 1
        playerman.isJump = False
        collide = False


            # collisions
        for slope in slopes:
            if playerman.rect.colliderect(slope.rect):
                collide = True
                playerman.isJump = False
                playerman.y = slope.rect.top - playerman.height   1
                if playerman.rect.right > slope.rect.left and playerman.rect.left < slope.rect.left - playerman.width:
                    playerman.x = slope.rect.left - playerman.width
                if playerman.rect.left < slope.rect.right and playerman.rect.right > slope.rect.right   playerman.width:
                    playerman.x = slope.rect.right


        if playerman.rect.bottom >= 890:
            collide = True
            playerman.isJump = False
            playerman.JumpCount = 10
            playerman.y = 890 - playerman.height



        if collide:
            if keys[pygame.K_SPACE]:
                playerman.isJump = True
                        
            playerman.fall = 0




    else:
        if playerman.JumpCount > 0:
            playerman.y -= (playerman.JumpCount*abs(playerman.JumpCount)) * 0.3
            playerman.JumpCount -= 1
        else:
            playerman.JumpCount = 10
            playerman.isJump = False


    

    

            

    redraw()
    pygame.display.update()

pygame.quit()

 

мои спрайты

введите описание изображения здесь

Комментарии:

1. Откуда вы взяли свое изображение? Похоже, это синусоидальная функция — у вас есть параметры?

Ответ №1:

Это вполне возможно, даже просто.

Я загрузил изображение «наклона» или земли и преобразовал его в массив numpy. После этого я нашел пиксели поверхности. Я использовал простое условие, чтобы определить, должен ли данный пиксель быть твердой поверхностью «воздуха»: если какой-либо из 4 каналов пикселя (= красный, зеленый, синий или альфа) больше нуля, этот пиксель считается твердой поверхностью. Если альфа-канал (последний) превышает некоторый порог, например 0 или 20, пиксель рассматривается как блок твердого грунта. Чтобы протестировать это, я сделал супер простой образец «игры», используя pygame. Игра загружает изображение в формате png (например, то, которое вы показали) и преобразует его.

Это решение использует одно и то же изображение как для обнаружения столкновений, так и для визуальных эффектов. Возможно, было бы неплохо добавить отдельный слой маски для столкновений вместо использования одного и того же файла. Если бы мы хотели использовать один и тот же файл, мы могли бы изменить контрастность и еще много чего, чтобы сделать изображение черно-белым, а затем применить к изображению какое-то определение границ.

ДЕМОНСТРАЦИЯ

Вот его gif в действии

Вот источник

Как это работает?

Допустим, у нас есть изображение, подобное этому, и мы хотим использовать его в качестве наклона. Это 8 пикселей в ширину и 5 пикселей в высоту. Я специально оставил там белый пиксель, поскольку на самом деле не имеет значения, что находится под поверхностью.

Наше демонстрационное растровое изображение

После того, как мы загрузили растровое изображение, оно выглядит следующим образом (у нас есть 2 канала для демонстрации, на самом деле не имеет значения, какие они — могут быть, например, интенсивность и альфа. На самом деле их было бы 4, для RGBA, но для демонстрации я выберу 2):

 >>> from PIL import Image
>>> import numpy as np

>>> bitmap_original = Image.open("hills1.png")
>>> bitmap = np.array(bitmap_original)

array([[ [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0] ],
       [ [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,1] ],
       [ [0,0], [0,1], [0,1], [0,0], [0,0], [0,1], [0,1], [0,1] ],
       [ [0,1], [0,1], [0,1], [0,1], [0,1], [0,1], [0,1], [0,1] ],
       [ [0,1], [0,1], [0,1], [0,1], [0,1], [0,1], [0,0], [0,1] ]])
 

Я использовал изображение из PIL для загрузки изображения, это должно быть возможно сделать и с pygame.

Давайте превратим его в 2d-массив, содержащий только истинностные значения. Здесь нас интересует только последнее значение (которое является альфа-каналом в pngs).

 >>> bitmap = bitmap[:,:,-1] > 0

array([[False, False, False, False, False, False, False, False],
       [False, False, False, False, False, False, False,  True],
       [False,  True,  True, False, False,  True,  True,  True],
       [ True,  True,  True,  True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True,  True, False,  True]])
 

Затем, чтобы найти первое «True» из каждого столбца, мы можем транспонировать массив и пройти через каждый вектор в цикле for (есть и более быстрые способы сделать это …), И когда мы находим первое True, мы сохраняем местоположение в тепловой карте и переходим к следующему столбцу.

 >>> bitmap_transposed = bitmap_2d.T


array([[False, False, False,  True,  True],
       [False, False,  True,  True,  True],
       [False, False,  True,  True,  True],
       [False, False, False,  True,  True],
       [False, False, False,  True,  True],
       [False, False,  True,  True,  True],
       [False, False,  True,  True, False],
       [False,  True,  True,  True,  True]])
 

Теперь массив готов, и мы можем легко выбрать значения y для каждого x.

 for x, col in enumerate(bitmap_transposed):
    for y, is_ground in enumerate(col): # 
    if is_ground:
        self.height_map[x] = y
 

Я реализовал это так в примере игры:

 from PIL import Image
import pygame as pg
import numpy as np

# Other code...


# This is called once during startup, possibly within a 'Game' class.
def img_to_map(self):

    bitmap = np.array(self.bitmap)

    # For each pixel: [0,0,0,0] => False, otherwise True
    # bitmap = np.any(bitmap, axis=2)

    # last element of each pixel value (= alpha) > 20 => True
    bitmap = bitmap[:,:,-1] > 20


    # initialize an empty array for the final height map
    self.height_map = np.zeros(bitmap.shape[1], dtype=int)
    for x, col in enumerate(bitmap.T):      # bitmap's transpose
        for y, is_ground in enumerate(col):

            # First ground pixel found (=surface)! Save the location
            # to the 'height map' and move to the next column.
            if is_ground:
                self.height_map[x] = int(y)
                break

# Other code...

# This method can be used to get the surface height in given x coordinate,
# which can then be used to detect collisions with
# the ground, move a player vertically or whatever...
def height_at(self, x):
    """
        Returns the ground height at given x coordinate.
        Principle:
            1. Check that the given coordinate lays somewhere within the ground.
            2. The corresponding height value is available in the height map
               that is generated during startup.
    """
    # self.position is an object that holds the ground offset position
    x_idx = x - self.position.x

    # object is outside the slope, cannot collide
    if x_idx < 0 or x_idx > (len(self.height_map) - 1):
        return 0

    # get the height value from the height map and add the ground offset value
    return self.position.y   self.height_map[x_idx]

# Other code...

 

Комментарии:

1. есть ли способ, которым я мог бы воспроизводить одно и то же изображение снова и снова, потому что я хочу, чтобы объект player продолжал двигаться в 1 направлении без остановки

2. кроме того, то, как вы это написали, для меня не имеет смысла, потому что мне нужен класс hell для повторения изображения, которое я создаю, как игра типа infinit run

3. что я пытаюсь сказать, как бы мне добавить еще один наземный объект проще, чем переделывать весь этот код

4. Да, конечно. Если бы я создавал бесконечный скроллер, я бы, вероятно, использовал какую-то сглаженную случайную функцию в качестве основной поверхности (тогда вам не нужно было бы выполнять какие-либо из этих операций с матрицей). Если бы я копировал одно и то же изображение, оно довольно быстро наскучило бы. Есть несколько способов обойти это. Возможно, используя что-то вроде этого .

5. Я отредактировал код, который вы мне прислали, и я пытаюсь сделать дублирование холмов, я поместил землю в виде списка, подобного этому, ground = [] затем добавил это ИЗОБРАЖЕНИЕ , но я получаю много ошибок, не могли бы вы помочь мне с этим вот последний код: pastebin.com/raw/W9GYgWqL << У меня есть спрайт игрока для этоготак что вы, вероятно, не можете запустить его