PyGame:Viewport click&拖动

时间:2013-07-16 15:06:08

标签: grid pygame drag viewport

我正在尝试复制某些网格视口的行为(例如 3ds Max 的正交视图)
或者映射像( GoogleMaps )的观众,我们有地图或网格,这是一个更大的 而不是屏幕,我们通过单击视口中的某个位置并拖动来导航。

到目前为止,我已经设法创建了一个非常大的网格,绘制它并使视口只显示它应该的图块。

到目前为止,这是我的代码:

import pygame, sys, math
from pygame.locals import *

FPS = 30
WINDOWWIDTH = 640
WINDOWHEIGHT = 480
GRIDWIDTH = 256
GRIDHEIGHT = 256
GRIDSIZE = 256
TILESIZE = 40
BGCOLOR = (128, 128, 128)
FGCOLOR = (64, 64, 64)

GRID = []

FPSCLOCK = pygame.time.Clock()

indexX = None
indexY = None

minVPcoordX = 0
minVPcoordY = 0
maxVPcoordX = (TILESIZE*GRIDSIZE)-WINDOWWIDTH
maxVPcoordY = (TILESIZE*GRIDSIZE)-WINDOWHEIGHT
viewportOffset = (0, 0)
vpStartXTile = 0
vpStartYTile = 0
viewportCoord = (0, 0)

coordX = 0
coordY = 0

movedDistanceX = 0
movedDistanceY = 0

speed = 4

def main():
    global FPSCLOCK, DISPLAYSURF
    global coordX, coordY
    global offsetX, offsetY, negativeOffsetX, negativeOffsetY
    global movedDistanceX, movedDistanceY

    DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))

    mouseX = 0
    mouseY = 0

    generateGrid(GRIDSIZE, GRIDSIZE)

    LeftButton = False
    mousePos = (0, 0)
    dragStart = (0,0)
    dragEnd = (0,0)

    pygame.font.init()
    arialFnt = pygame.font.SysFont('Arial', 16)
    while True:
        DISPLAYSURF.fill(BGCOLOR)

        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

        #X
        if coordX < maxVPcoordX:
            coordX += speed
        elif coordX < minVPcoordX:
            coordX = 0
        else:
            coordX = maxVPcoordX
        #Y   
        if coordY < maxVPcoordY:
            coordY += speed
        elif coordY < minVPcoordY:
            coordY = 0
        else:
            coordY = maxVPcoordY
        #-------------

        viewportCoord = (coordX, coordY)
        print(coordX, coordY)

        vpStartXTile = math.floor(float(viewportCoord[0]/TILESIZE))
        vpStartYTile = math.floor(float(viewportCoord[1]/TILESIZE))
        GRIDstartTile = GRID[vpStartXTile][vpStartYTile]

        negativeOffsetX = viewportCoord[0] - GRID[vpStartXTile][vpStartYTile][0]
        negativeOffsetY = viewportCoord[1] - GRID[vpStartXTile][vpStartYTile][1]

        offsetX = TILESIZE - negativeOffsetX
        offsetY = TILESIZE - negativeOffsetY

        repeatX = math.floor(WINDOWWIDTH/TILESIZE)
        repeatY = math.floor(WINDOWHEIGHT/TILESIZE)

        drawGrid(repeatX, repeatY)

        outputLabel = arialFnt.render('(Top-Left)Coordinates: x%s - y%s' % (coordX, coordY), 1, (255,255,255))
        DISPLAYSURF.blit(outputLabel, (10, 10))

        # frame draw
        pygame.display.set_caption("Memory Game - FPS: %.0f" % FPSCLOCK.get_fps())
        pygame.display.flip()
        pygame.display.update()
        FPSCLOCK.tick(FPS)



def generateGrid(xTiles=None, yTiles=None):
    global GRID

    GRID = []

    for i in range(xTiles):
        GRID.append([None] * yTiles)

    ix = 0
    iy = 0

    posX = -40

    for x in range(len(GRID[ix])):
        posX += TILESIZE
        posY = -40
        iy = 0
        for y in range(xTiles):
            posY += TILESIZE
            position = (posX, posY)
            GRID[ix][iy] = position
            iy += 1
        if ix < xTiles:
            ix += 1
        else:
            return

def drawGrid(x=None, y=None):
    lineWidth = 1

    xPos = 0
    yPos = 0

    for i in range(x):
        xStart = (xPos + offsetX, 0)
        xEnd = (xPos + offsetX, WINDOWHEIGHT + negativeOffsetY)
        pygame.draw.line(DISPLAYSURF, FGCOLOR, xStart, xEnd, lineWidth)
        xPos += TILESIZE

    for i in range(y):
        yStart = (0, yPos + offsetY)
        yEnd = (WINDOWWIDTH + negativeOffsetX, yPos + offsetY)
        pygame.draw.line(DISPLAYSURF, FGCOLOR, yStart, yEnd, lineWidth)
        yPos += TILESIZE

def moveGrid():    
    pass

def zoomIn():
    pass

def zoomOut():
    pass

main()    

正如您所看到的,它按预期工作(我没有实现任何形式的点击和拖动  在这个样本中)。

似乎 pygame 没有此事件,所以它必须是一个组合 MOUSEBUTTONDOWN MOUSEMOTION

我尝试用 get_pos()存储前一帧的位置并减去当前帧的位置,但我无法弄清楚下一帧的位置。  它的加速速度太快了。
我也尝试使用鼠标的 get_rel()方法,但没有成功。
(虽然我很确定我不应该把鼠标位置放到屏幕位置)

我研究周围是否有其他人这样做,但我发现只有如何拖动某些东西 在固定的屏幕上。我需要相反 - 在固定网格上拖动屏幕。

所以,如果有人对如何制作这个机制有任何想法或建议,或者我可以研究它的任何链接,我将不胜感激!

ps:我发现了类似的东西,但它是用JS编写的,翻译很痛苦)

1 个答案:

答案 0 :(得分:1)

我搞定了!

它仍然存在一些关于zoomIn / zoomOut添加的问题,但拖动的主要问题是 周围的网格是固定的。

import pygame, sys, math
from pygame.locals import *

FPS = 30
WINDOWWIDTH = 640
WINDOWHEIGHT = 480
GRIDSIZE = 256
TILESIZE = 40
BGCOLOR = (128, 128, 128)
FGCOLOR = (64, 64, 64)

GRID = []

FPSCLOCK = pygame.time.Clock()

indexX = None
indexY = None

minVPcoordX = 0
minVPcoordY = 0
maxVPcoordX = (TILESIZE*GRIDSIZE)-WINDOWWIDTH
maxVPcoordY = (TILESIZE*GRIDSIZE)-WINDOWHEIGHT
viewportOffset = (0, 0)
vpStartXTile = 0
vpStartYTile = 0
viewportCoord = (0, 0)

coordX = 0
coordY = 0

def main():
    global FPSCLOCK, DISPLAYSURF
    global coordX, coordY
    global offsetX, offsetY, negativeOffsetX, negativeOffsetY
    global movedDistanceX, movedDistanceY
    global isDragging

    DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))

    mouseX = 0
    mouseY = 0

    generateGrid(GRIDSIZE, GRIDSIZE)

    isDragging = False
    mousePos = (0, 0)
    dragStart = (0,0)
    dragEnd = (0,0)

    pygame.font.init()
    arialFnt = pygame.font.SysFont('Arial', 16)
    while True:
        DISPLAYSURF.fill(BGCOLOR)

        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
            if event.type == MOUSEBUTTONDOWN:
                if event.button == 2:
                    isDragging = True
                elif event.button == 4:
                    zoomIn()
                elif event.button == 5:
                    zoomOut()
            elif event.type == MOUSEMOTION:
                mouseRel = pygame.mouse.get_rel()
                moveGrid(mouseRel)
            elif event.type == MOUSEBUTTONUP:
                isDragging = False

        viewportCoord = (coordX, coordY)

        vpStartXTile = math.floor(float(viewportCoord[0]/TILESIZE))
        vpStartYTile = math.floor(float(viewportCoord[1]/TILESIZE))
        GRIDstartTile = GRID[vpStartXTile][vpStartYTile]

        negativeOffsetX = viewportCoord[0] - GRID[vpStartXTile][vpStartYTile][0]
        negativeOffsetY = viewportCoord[1] - GRID[vpStartXTile][vpStartYTile][1]

        offsetX = TILESIZE - negativeOffsetX
        offsetY = TILESIZE - negativeOffsetY

        repeatX = math.floor(WINDOWWIDTH/TILESIZE)
        repeatY = math.floor(WINDOWHEIGHT/TILESIZE)

        drawGrid(repeatX, repeatY)

        outputLabel = arialFnt.render('(Top-Left)Coordinates: x%s - y%s' % (coordX, coordY), 1, (255,255,255))
        DISPLAYSURF.blit(outputLabel, (10, 10))

        # frame draw
        pygame.display.set_caption("Memory Game - FPS: %.0f" % FPSCLOCK.get_fps())
        pygame.display.flip()
        pygame.display.update()
        FPSCLOCK.tick(FPS)



def generateGrid(xTiles=None, yTiles=None):
    global GRID

    GRID = []

    for i in range(xTiles):
        GRID.append([None] * yTiles)

    ix = 0
    iy = 0

    posX = -40

    for x in range(len(GRID[ix])):
        posX += TILESIZE
        posY = -40
        iy = 0
        for y in range(xTiles):
            posY += TILESIZE
            position = (posX, posY)
            GRID[ix][iy] = position
            iy += 1
        if ix < xTiles:
            ix += 1
        else:
            return

def drawGrid(x=None, y=None):
    lineWidth = 1

    xPos = 0
    yPos = 0

    for i in range(x):
        xStart = (xPos + offsetX, 0)
        xEnd = (xPos + offsetX, WINDOWHEIGHT + negativeOffsetY)
        pygame.draw.line(DISPLAYSURF, FGCOLOR, xStart, xEnd, lineWidth)
        xPos += TILESIZE

    for i in range(y):
        yStart = (0, yPos + offsetY)
        yEnd = (WINDOWWIDTH + negativeOffsetX, yPos + offsetY)
        pygame.draw.line(DISPLAYSURF, FGCOLOR, yStart, yEnd, lineWidth)
        yPos += TILESIZE

def moveGrid(rel):
    global coordX, coordY, isDragging


    if isDragging == True:
        #X
        if coordX <= maxVPcoordX and coordX >= minVPcoordX:
            coordX = coordX - rel[0]
            if coordX > maxVPcoordX:
                coordX = maxVPcoordX
            if coordX < minVPcoordX:
                coordX = 0
        #Y   
        if coordY <= maxVPcoordY and coordY >= minVPcoordY:
            coordY = coordY - rel[1]
            if coordY > maxVPcoordY:
                coordY = maxVPcoordY
            elif coordY < minVPcoordY:
                coordY = 0
    #-------------



def zoomIn():
    global TILESIZE, maxVPcoordX, maxVPcoordY

    TILESIZE += 4

    print("Tile size: ", TILESIZE)

    generateGrid(GRIDSIZE, GRIDSIZE)

    maxVPcoordX = (TILESIZE*GRIDSIZE)-WINDOWWIDTH
    maxVPcoordY = (TILESIZE*GRIDSIZE)-WINDOWHEIGHT


def zoomOut():
    global TILESIZE, maxVPcoordX, maxVPcoordY

    TILESIZE -= 4
    if TILESIZE <= 0:
        TILESIZE = 4

    print("Tile size: ", TILESIZE)

    generateGrid(GRIDSIZE, GRIDSIZE)

    maxVPcoordX = (TILESIZE*GRIDSIZE)-WINDOWWIDTH
    maxVPcoordY = (TILESIZE*GRIDSIZE)-WINDOWHEIGHT

main()