1
\$\begingroup\$

The game I am working with is a block game. It generates terrain and then allows you to freely mine and place blocks as you wish (sound familiar Cx) BUT! there is a problem. I have a code that snaps the mouse to a 32x32 grid:

mse=pygame.mouse.get_pos()
mse=(((mse[0])/32)*32,((mse[1])/32)*32)
cursrect.x,cursrect.y=mse
screen.blit(curs,cursrect)

This draws the cursor and snaps it to 32x32 grid. But the problem is, I have the sidescrolling code:

for p in players:
    if p.rect.right>=576:
        for b in instancelist:
            b.rect.right-=4
        p.rect.right-=4
    if p.rect.left<=64:
        for b in instancelist:
            b.rect.left+=4
        p.rect.left+=4

Now when the player moves the screen, the mouse is offset from the rest of the world and doesn't place the blocks to a 32x32 grid relative to the rest of the blocks.. so.. how can I fix this?

Here is the whole code: import pygame,random from collections import namedtuple from pygame.locals import * pygame.init() pygame.display.set_caption('PiBlocks 0.1| By Sam Tubb') screen=pygame.display.set_mode((640,480)) instancelist=[] players=[] clock=pygame.time.Clock() texdir='org_texture' Move = namedtuple('Move', ['up', 'left', 'right']) ingame=0 max_gravity = 100

#load sprites
grass=pygame.image.load(texdir+'\\grass.png').convert()
dirt=pygame.image.load(texdir+'\\dirt.png').convert()
stone=pygame.image.load(texdir+'\\stone.png').convert()
psprite=pygame.image.load(texdir+'\\player.png').convert()
curs=pygame.image.load(texdir+'\\cursor.png').convert()
curs.set_colorkey((0,255,0))
cursrect=curs.get_rect(x=32,y=32)
blocksel=dirt

class Player(object):
    sprite=psprite
    def __init__(self, x, y):
        self.rect = self.sprite.get_rect(centery=y, centerx=x)
        # indicates that we are standing on the ground
        # and thus are "allowed" to jump
        self.on_ground = True
        self.xvel = 0
        self.yvel = 0
        self.jump_speed = 7
        self.move_speed = 3

    def update(self, move, blocks):

        # check if we can jump 
        if move.up and self.on_ground:
            self.yvel -= self.jump_speed

        # simple left/right movement
        if move.left:
                self.xvel = -self.move_speed
        if move.right:
                self.xvel = self.move_speed

        # if in the air, fall down
        if not self.on_ground:
            self.yvel += 0.3
            # but not too fast
            if self.yvel > max_gravity: self.yvel = max_gravity

        # if no left/right movement, x speed is 0, of course
        if not (move.left or move.right):
            self.xvel = 0

        # move horizontal, and check for horizontal collisions
        self.rect.left += self.xvel
        self.collide(self.xvel, 0, blocks)

        # move vertically, and check for vertical collisions
        self.rect.top += self.yvel
        self.on_ground = False;
        self.collide(0, self.yvel, blocks)

    def collide(self, xvel, yvel, blocks):
        # all blocks that we collide with
        for block in [blocks[i] for i in self.rect.collidelistall(blocks)]:

            # if xvel is > 0, we know our right side bumped 
            # into the left side of a block etc.
            if xvel > 0:
                    self.rect.right = block.rect.left
            if xvel < 0:
                    self.rect.left = block.rect.right

            # if yvel > 0, we are falling, so if a collision happpens 
            # we know we hit the ground (remember, we seperated checking for
            # horizontal and vertical collision, so if yvel != 0, xvel is 0)
            if yvel > 0:
                self.rect.bottom = block.rect.top
                self.on_ground = True
                self.yvel = 0
            # if yvel < 0 and a collision occurs, we bumped our head
            # on a block above us
            if yvel < 0: self.rect.top = block.rect.bottom

class Block(object):
    def __init__(self,x,y,sprite):
        self.x=x
        self.y=y
        self.sprite=sprite
        self.rect=self.sprite.get_rect(x=self.x,y=self.y)

top=(random.randint(5,8)*32)
cen=(top+random.randint(4,6)*32)
down=15
across=0
blklvl=0

while across<3200:
        while down>0:
                screen.fill((0,0,0))
                if blklvl==top:
                        blocksel=grass
                        instancelist.append(Block(across,blklvl,blocksel))
                if blklvl>top:
                        if blklvl<cen:
                                blocksel=dirt
                                instancelist.append(Block(across,blklvl,blocksel))
                if blklvl>cen-1:
                        blocksel=stone
                        instancelist.append(Block(across,blklvl,blocksel))
                down=down-1
                blklvl=blklvl+32

        if down==0:
                if across<3200:
                        per=(across/(32/5))
                        if per>100:
                                per=100
                        top=(random.randint(5,8)*32)
                        cen=(top+random.randint(4,6)*32)
                        down=15 
                        blklvl=0
                        across=across+32
                        down=15
                        #print 'GENERATION:'+str(per)+'%'
                        pygame.display.flip()

players.append(Player(640/2,20))
blocksel=dirt
#mainloop
while True:
    #block select
    key=pygame.key.get_pressed()

    if key[K_1]:
        blocksel=grass
    if key[K_2]:
        blocksel=dirt
    if key[K_3]:
        blocksel=stone

    #manual camera
    if key[K_UP]:
        for b in instancelist:
            b.rect.top+=32
        for p in players:
            p.rect.top+=32
    if key[K_DOWN]:
        for b in instancelist:
            b.rect.bottom-=32
        for p in players:
            p.rect.bottom-=32
    if key[K_RIGHT]:
        for b in instancelist:
            b.rect.right-=32
        for p in players:
            p.rect.right-=32
    if key[K_LEFT]:
        for b in instancelist:
            b.rect.left+=32
        for p in players:
            p.rect.left+=32

    screen.fill((20,70,255))
    mse=pygame.mouse.get_pos()
    move = Move(key[K_w], key[K_a], key[K_d])
    for e in pygame.event.get():
        if e.type==QUIT:
            exit(1)
        if e.type==MOUSEBUTTONUP:
            to_remove = [b for b in instancelist if b.rect.collidepoint(mse)]
            for b in to_remove:
                instancelist.remove(b)
            if not to_remove:
                mse=(((mse[0])/32)*32,((mse[1])/32)*32)
                instancelist.append(Block(mse[0],mse[1],blocksel))
    for inst in instancelist:
        screen.blit(inst.sprite,inst.rect)
    clock.tick(60)
    for p in players:
        if p.rect.right>=576:
            for b in instancelist:
                b.rect.right-=4
            p.rect.right-=4
        if p.rect.left<=64:
            for b in instancelist:
                b.rect.left+=4
            p.rect.left+=4

        p.update(move, instancelist)
        for b in instancelist:
            if p.rect.colliderect(b.rect):
                p.ground=1
        mse=pygame.mouse.get_pos()
        mse=(((mse[0])/32)*32,((mse[1])/32)*32)
        cursrect.x,cursrect.y=mse
        screen.blit(curs,cursrect)
        screen.blit(psprite,p.rect)
    pygame.display.flip()

Thanks :)

\$\endgroup\$
2
\$\begingroup\$

Here are some suggestions to get you going:

  • Don't keep blocks in the list. Perhaps, create some ordered structure (e.g. Grid) where you can easily insert or query blocks (and you'll need that for faster collision detection later on)?
  • Create camera and decouple it, don't change position of the items in the world. You should add some methods which would transform your screen coordinates to world coordinates, and vice-versa.

For the second part as it relates to your question, in your camera class:

class Camera:
    def __init__(self, x, y)
        self.xPos = x
        self.yPos = y
    def pointToWorld(self, screenX, screenY):
        return {'x': screenX + self.xPos, 'y': screenY + self.yPos} #you should use and return Point

Here's the flow:

  1. Obtain your click location
  2. use pointToWorld to convert it to world location
  3. use world location to figure out where in the world have you clicked and do something accordingly.

Naturally, first thing to do is create a camera class that you can move, implement complementary function (worldToScreen?), and change your rendering code to reflect that.

\$\endgroup\$
  • \$\begingroup\$ I'll see what I can do with it. I'm not to skilled at this yet though. \$\endgroup\$ – Sam Tubb Oct 11 '13 at 10:34
  • 1
    \$\begingroup\$ You already have everything in place basically. For start, try changing the lines which draw blocks to use camera, and go from there. \$\endgroup\$ – Alex Oct 12 '13 at 13:28

Not the answer you're looking for? Browse other questions tagged or ask your own question.