Pygame : Pyscroll & Pytmx working on Tiled - Isometric map [duplicate] - dictionary

I am facing some issues while trying to create a staggered isomteric game map with pygame.
So far I can draw a rectangular one but I have no clue on how to rotate it.
Here is the code:
from pygame.locals import *
import pygame
green = (40,255,30)
brown = (40,60,90)
grass = 0
dirt = 1
colours = {
grass: green,
dirt: brown,
}
tilemap = [
[grass,dirt,dirt,dirt, grass],
[dirt,grass,dirt,dirt, dirt],
[grass, grass,dirt,dirt, grass],
[grass, grass,dirt,dirt, grass],
[dirt,dirt,dirt,dirt,grass]
]
TILESIZE = 50
MAPWIDTH = 5
MAPHEIGHT = 5
pygame.init()
DISPLAYMAP = pygame.display.set_mode((MAPWIDTH*TILESIZE,MAPHEIGHT*TILESIZE))
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
DISPLAYMAP.fill((0, 0, 0))
for row in range(MAPWIDTH):
print
for column in range(MAPHEIGHT):
color = colours[tilemap[row][column]]
pygame.draw.rect(DISPLAYMAP, color, (column * TILESIZE, row * TILESIZE, TILESIZE, TILESIZE))
pygame.display.update()
Here is the result:
And I would like to have something looking like this:
Any help would be really appreciated.
Thank you

Use pygame.transform.rotate() and pygame.transform.scale() to create an isometric pygame.Surface object for each kind of tile:
isometric_tiles = {}
for key, color in colours.items():
tile_surf = pygame.Surface((TILESIZE, TILESIZE), pygame.SRCALPHA)
tile_surf.fill(color)
tile_surf = pygame.transform.rotate(tile_surf, 45)
isometric_size = tile_surf.get_width()
tile_surf = pygame.transform.scale(tile_surf, (isometric_size, isometric_size//2))
isometric_tiles[key] = tile_surf
blit the tiles instead of drawing rectangles:
for column in range(MAPWIDTH):
for row in range(MAPHEIGHT):
tile_surf = isometric_tiles[tilemap[row][column]]
x = (column + (MAPHEIGHT - row)) * isometric_size // 2
y = 20 + (column + row) * isometric_size // 4
DISPLAYMAP.blit(tile_surf, (x, y))
Complete example:
from pygame.locals import *
import pygame
green = (40,255,30)
brown = (40,60,90)
grass = 0
dirt = 1
colours = {
grass: green,
dirt: brown,
}
tilemap = [
[grass,dirt,dirt,dirt, grass],
[dirt,grass,dirt,dirt, dirt],
[grass, grass,dirt,dirt, grass],
[grass, grass,dirt,dirt, grass],
[dirt,dirt,dirt,dirt,grass]
]
TILESIZE = 50
MAPWIDTH = 5
MAPHEIGHT = 5
isometric_tiles = {}
for key, color in colours.items():
tile_surf = pygame.Surface((TILESIZE, TILESIZE), pygame.SRCALPHA)
tile_surf.fill(color)
tile_surf = pygame.transform.rotate(tile_surf, 45)
isometric_size = tile_surf.get_width()
tile_surf = pygame.transform.scale(tile_surf, (isometric_size, isometric_size//2))
isometric_tiles[key] = tile_surf
pygame.init()
DISPLAYMAP = pygame.display.set_mode((MAPWIDTH*TILESIZE*2,MAPHEIGHT*TILESIZE))
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
DISPLAYMAP.fill((0, 0, 0))
for column in range(MAPWIDTH):
for row in range(MAPHEIGHT):
tile_surf = isometric_tiles[tilemap[row][column]]
x = (column + (MAPHEIGHT - row)) * isometric_size // 2
y = 20 + (column + row) * isometric_size // 4
DISPLAYMAP.blit(tile_surf, (x, y))
pygame.display.update()
For the staggered representation you have to change the arrangement of the tiles:
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
DISPLAYMAP.fill((0, 0, 0))
for column in range(MAPWIDTH):
for row in range(MAPHEIGHT):
tile_surf = isometric_tiles[tilemap[row][column]]
x = 20 + column * isometric_size + (row % 2) * isometric_size // 2
y = 20 + row * isometric_size // 4
DISPLAYMAP.blit(tile_surf, (x, y))
pygame.display.update()

Related

Dynamic (auto increment) input length (count) on a linear regression channel drawing, starting from given bar_index/datetime in Pine Script language

My question is about the Linear Regression drawing.
The example in the documentation uses a fixed length (100), and is therefore :
shifting to the right on each new bar
of constant width (here 100 bars)
I'm trying to make it start from a custom point in time (x bars from now or bar_index or datetime...), so that :
it keeps extending on each new bar
but the starting point remains on the same location (until we change it in the settings).
That means that the length (input) would be dynamic and increase on each new bar.
I am getting the following error : Pine cannot determine the referencing length of a series. Try using max_bars_back in the study or strategy function.
Is it possible to do ?
Here is the code
//#version=4
study("Linear Regression", shorttitle="LinReg", overlay=true)
upperMult = input(title="Upper Deviation", defval=2)
lowerMult = input(title="Lower Deviation", defval=-2)
useUpperDev = input(title="Use Upper Deviation", defval=true)
useLowerDev = input(title="Use Lower Deviation", defval=true)
showPearson = input(title="Show Pearson's R", defval=true)
extendLines = input(title="Extend Lines", defval=false)
// ====================================================================
// ====================================================================
// Original parameter (the one that should increments)
// len = input(title="Count", defval=100)
// Unsuccessful attempt : "Starting from given bar_index"
barIndexOfStartingBar = 6392 - 80 // 6392 : Current bar_index, 80 : Offset to the starting bar
len = bar_index - barIndexOfStartingBar
len := nz(len[1]) + 1
// Unsuccessful attempt : "x bars from current bar"
startingPointFromCurrentBar = input(title="Count", defval=80)
len = (bar_index + startingPointFromCurrentBar) - bar_index
len := nz(len[1]) + 1
// ====================================================================
// ====================================================================
src = input(title="Source", defval=close)
extend = extendLines ? extend.right : extend.none
calcSlope(src, len) =>
if not barstate.islast or len <= 1
[float(na), float(na), float(na)]
else
sumX = 0.0
sumY = 0.0
sumXSqr = 0.0
sumXY = 0.0
for i = 0 to len - 1
val = src[i]
per = i + 1.0
sumX := sumX + per
sumY := sumY + val
sumXSqr := sumXSqr + per * per
sumXY := sumXY + val * per
slope = (len * sumXY - sumX * sumY) / (len * sumXSqr - sumX * sumX)
average = sumY / len
intercept = average - slope * sumX / len + slope
[slope, average, intercept]
[s, a, i] = calcSlope(src, len)
startPrice = i + s * (len - 1)
endPrice = i
var line baseLine = na
if na(baseLine) and not na(startPrice)
baseLine := line.new(bar_index - len + 1, startPrice, bar_index, endPrice, width=1, extend=extend, color=color.red)
else
line.set_xy1(baseLine, bar_index - len + 1, startPrice)
line.set_xy2(baseLine, bar_index, endPrice)
na
calcDev(src, len, slope, average, intercept) =>
upDev = 0.0
dnDev = 0.0
stdDevAcc = 0.0
dsxx = 0.0
dsyy = 0.0
dsxy = 0.0
periods = len - 1
daY = intercept + (slope * periods) / 2
val = intercept
for i = 0 to periods
price = high[i] - val
if (price > upDev)
upDev := price
price := val - low[i]
if (price > dnDev)
dnDev := price
price := src[i]
dxt = price - average
dyt = val - daY
price := price - val
stdDevAcc := stdDevAcc + price * price
dsxx := dsxx + dxt * dxt
dsyy := dsyy + dyt * dyt
dsxy := dsxy + dxt * dyt
val := val + slope
stdDev = sqrt(stdDevAcc / (periods == 0 ? 1 : periods))
pearsonR = dsxx == 0 or dsyy == 0 ? 0 : dsxy / sqrt(dsxx * dsyy)
[stdDev, pearsonR, upDev, dnDev]
[stdDev, pearsonR, upDev, dnDev] = calcDev(src, len, s, a, i)
upperStartPrice = startPrice + (useUpperDev ? upperMult * stdDev : upDev)
upperEndPrice = endPrice + (useUpperDev ? upperMult * stdDev : upDev)
var line upper = na
lowerStartPrice = startPrice + (useLowerDev ? lowerMult * stdDev : -dnDev)
lowerEndPrice = endPrice + (useLowerDev ? lowerMult * stdDev : -dnDev)
var line lower = na
if na(upper) and not na(upperStartPrice)
upper := line.new(bar_index - len + 1, upperStartPrice, bar_index, upperEndPrice, width=1, extend=extend, color=#0000ff)
else
line.set_xy1(upper, bar_index - len + 1, upperStartPrice)
line.set_xy2(upper, bar_index, upperEndPrice)
na
if na(lower) and not na(lowerStartPrice)
lower := line.new(bar_index - len + 1, lowerStartPrice, bar_index, lowerEndPrice, width=1, extend=extend, color=#0000ff)
else
line.set_xy1(lower, bar_index - len + 1, lowerStartPrice)
line.set_xy2(lower, bar_index, lowerEndPrice)
na
// Pearson's R
var label r = na
transparent = color.new(color.white, 100)
label.delete(r[1])
if showPearson and not na(pearsonR)
r := label.new(bar_index - len + 1, lowerStartPrice, tostring(pearsonR, "#.################"), color=transparent, textcolor=#0000ff, size=size.normal, style=label.style_labelup)
With these changes you should be able to go back a few thousand bars:
study("Linear Regression", shorttitle="LinReg", overlay=true, max_bars_back = 4999)
and either one of these:
// #1
offsetToStart = input(100, minval = 1, step = 100)
len = max(1, offsetToStart)
// #2
startingBar = input(10000, minval = 1, step = 100)
len = max(1, bar_index - startingBar)
What you are looking for is called anchoring. You can do this with a trick. What I usually do is the following:
p = input("D", title="Period", type=input.resolution)
offsetToStart = input(0, title="Begin Offset") // remove the minval
newbar(p) => change(time(p)) == 0?0:1
nb = newbar(p)
bars = barssince(nb) + offsetToStart
var line l1 = na
l1 := line.new(bar_index[bars], open, bar_index[bars], open + syminfo.mintick, extend=extend.both)
line.delete(l1[1])
Remember to remove the minval. Chose the period and you can now anchor beginning of your indicator. Playaround with p

Ammo.js custom mesh collision with sphere

I'm trying to generate collisions for every object/mesh. They are all static and should collide with a ball/sphere, which is dynamic.
My code looks like this:
const transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(-1.1726552248001099, 2.6692488193511963, 0));
transform.setRotation(new Ammo.btQuaternion(0.5, -0.5, 0.5, 0.4999999701976776));
const motionState = new Ammo.btDefaultMotionState(transform);
// Vertices and indices are parsed by GLTF parser by loaders.gl
const vertices = Entity.vertices;
const indices = Entity.indices;
const scale = [0.15933185815811157, 1.1706310510635376, 0.15933185815811157];
// btConvexHullShape or btBvhTriangleMeshShape, see below.
const localInertia = new Ammo.btVector3(0, 0, 0);
const rbInfo = new Ammo.btRigidBodyConstructionInfo(0, motionState, shape, localInertia);
const object = new Ammo.btRigidBody(rbInfo);
this._physicsWorld.addRigidBody(object);
I tried 2 methods: btConvexHullShape and btBvhTriangleMeshShape and both did not work.
btConvexHullShape
const shape = new Ammo.btConvexHullShape();
for (let i = 0; i < vertices.length / 3; i++) {
shape.addPoint(new Ammo.btVector3(vertices[i * 3] * scale[0], vertices[i * 3 + 1] * scale[1], vertices[i * 3 + 2] * scale[2]));
}
Path of the sphere using btConvexHullShape is something like this (it doesn't enter the hole):
I'm guessing Ammo.js connects top-most points? How can I control which points are connected based on indices? It also seems that this approach is very slow (30k vertices takes some time), so I tried:
btBvhTriangleMeshShape
const mesh = new Ammo.btTriangleMesh(true, true);
for (let i = 0; i * 3 < indices.length; i++) {
mesh.addTriangle(
new Ammo.btVector3(vertices[indices[i * 3]] * scale[0], vertices[indices[i * 3] + 1] * scale[1], vertices[indices[i * 3] + 2] * scale[2]),
new Ammo.btVector3(vertices[indices[i * 3 + 1]] * scale[0], vertices[indices[i * 3 + 1] + 1] * scale[1], vertices[indices[i * 3 + 1] + 2] * scale[2]),
new Ammo.btVector3(vertices[indices[i * 3 + 2]] * scale[0], vertices[indices[i * 3 + 2] + 1] * scale[1], vertices[indices[i * 3 + 2] + 2] * scale[2]),
false
);
}
const shape = new Ammo.btBvhTriangleMeshShape(mesh, true, true);
This method is a bit better, but the sphere just goes through the object when it bounces once (bounces off the circle and goes through the object (red circle)):
The reason why btConvexHullShape wasn't working (although the collision around it actually works) is because it isn't for concave shapes.
As for the btBvhTriangleMeshShape, I have been inputting the vertices the wrong way. I had to multiply the indices by 3 (because each vertex has 3 components).
Here's the full working code:
const transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(-1.1726552248001099, 2.6692488193511963, 0));
transform.setRotation(new Ammo.btQuaternion(0.5, -0.5, 0.5, 0.4999999701976776));
const motionState = new Ammo.btDefaultMotionState(transform);
// Vertices and indices are parsed by GLTF parser by loaders.gl
const vertices = Entity.vertices;
const indices = Entity.indices;
const scale = [0.15933185815811157, 1.1706310510635376, 0.15933185815811157];
const mesh = new Ammo.btTriangleMesh(true, true);
mesh.setScaling(new Ammo.btVector3(scale[0], scale[1], scale[2]));
for (let i = 0; i * 3 < indices.length; i++) {
mesh.addTriangle(
new Ammo.btVector3(vertices[indices[i * 3] * 3], vertices[indices[i * 3] * 3 + 1], vertices[indices[i * 3] * 3 + 2]),
new Ammo.btVector3(vertices[indices[i * 3 + 1] * 3], vertices[indices[i * 3 + 1] * 3 + 1], vertices[indices[i * 3 + 1] * 3 + 2]),
new Ammo.btVector3(vertices[indices[i * 3 + 2] * 3], vertices[indices[i * 3 + 2] * 3 + 1], vertices[indices[i * 3 + 2] * 3 + 2]),
false
);
}
const shape = new Ammo.btBvhTriangleMeshShape(mesh, true, true);
const localInertia = new Ammo.btVector3(0, 0, 0);
const rbInfo = new Ammo.btRigidBodyConstructionInfo(0, motionState, shape, localInertia);
const object = new Ammo.btRigidBody(rbInfo);
this._physicsWorld.addRigidBody(object);

create multiple buttons from table in lua ( Corona sdk )

I have a table which looks like this:
table =
{
{
id = 1,
name = 'john',
png = 'john.png',
descr = "..."
},
{
id = 2,
name = 'sam',
png = "sam.png",
descr = "..."
}
...
}
What function could I use to display each picture like this and make them buttons
so that when I click on their image I can open their info.
This is where I am stuck:
local buttons = display.newGroup()
local xpos = -20
local ypos = 0
local e = -1
function addpicture ()
for i=1, #table do
xpos = (xpos + 100) % 300
e = e + 1
ypos = math.modf((e)*1/3) * 100 + 100
local c = display.newImage( table[i].name, system.TemporaryDirectory, xpos, ypos)
c:scale( 0.4, 0.4 )
c.name = table[i].tvname
buttons:insert(c)
end
end
function buttons:touch( event )
if event.phase == "began" then
print(self, event.id)
end
end
buttons:addEventListener('touch', buttons)
addpicture()
How can I recognize which image is touched in order to go back to the persons info?
I solved my problem by adding the listener inside of the loop like this:
function addpicture ()
for i=1, #table do
xpos = (xpos + 100) % 300
e = e + 1
ypos = math.modf((e)*1/3) * 100 + 100
local c = display.newImage( table[i].name, system.TemporaryDirectory, xpos, ypos)
c:scale( 0.4, 0.4 )
c.name = table[i].tvname
buttons:insert(c)
function c:touch( event )
if event.phase == "began" then
print(self, event.id)
end
end
c:addEventListener('touch', c)
end
end
addpicture()

Update canvas without delete the existing objects

I have a QCanvas with multiple objects on it. How could I update the canvas and put new object onto it without delete the existing ones?
I want the canvas to draw every existing objects, even if I draw a new one. A new object is generated on every mouse event.
class CANVAS(QtGui.QWidget):
def __init__(self , parent):
super(CANVAS , self).__init__(parent)
self.setGeometry( 0 , 30 , 530 , 530 )
self.frame = QtGui.QFrame(self)
self.CLICKED = 1
self.FUNCTION = 0
self.x =""
self.y =""
def paintEvent(self, e):
qp = QtGui.QPainter()
qp.begin(self)
self.drawPoints(qp)
qp.end()
def drawPoints(self, qp):
qp.setPen(QtCore.Qt.red)
t = points.point()
print self.x
if self.CLICKED == 1:
qp.drawRect(int(self.x), int(self.y), 10, 10)
self.CLICKED = 0
if self.FUNCTION == 1:
for k in range(0,360,1):
radius = 50
a = float(self.x) + radius * np.cos(k)
b = float(self.y) + radius * np.sin(k)
qp.drawPoint(a,b)
qp.drawPoint(int(self.x), int(self.y))
print "circle done"
self.FUNCTION = 0
elif self.FUNCTION == 2:
start_P = points.point(int(self.x), int(self.y))
a = 15
b = 20
upperL = points.point((int(self.x) + (10 * a)), int(self.y))
P = [start_P, upperL]
dummy1 = LinInt(P, qp)
leftL = points.point((int(self.x)), ((int(self.y))+(10*b)))
P = [start_P, leftL]
dummy2 = LinInt(P, qp)
tmp = dummy1.pop()
rightL = points.point((tmp.getX()),((int(self.y))+(10*b)))
P = [tmp, rightL]
LinInt(P, qp)
P = [leftL, rightL]
LinInt(P, qp)
self.FUNCTION = 0
print "rectangle done"
elif self.FUNCTION == 3:
print "curve"
def mousePressEvent(self, event):
self.x = ""
self.y = ""
coordinates = event.x()
for i in str(coordinates):
if i.isdigit():
self.x+=i
coordinates = event.y()
for i in str(coordinates):
if i.isdigit():
self.y+=i
self.CLICKED = 1
self.update()
In the constructor, create an empty list. In your drawPoints, do:
if self.CLICKED == 1:
rect = QRect(int(self.x), int(self.y), 10, 10)
self.rects.append(rect)
qp.drawRects(self.rects)

PyGame: Viewport click & Drag

I'm trying to duplicate the behavior of some grid viewports (like 3ds Max's Orthometric view)
or map viewers like (GoogleMaps) where we have the map or grid, which is a whole lot bigger
than the screen, and we navigate by clicking somewhere in the viewport and dragging.
So far, i've managed to create a pretty big grid, draw it and make the viewport show only the tiles it should.
Here is my code so far:
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()
As you can see, it works as expected (i haven't implemented any form of click&drag
in this sample).
It seems that pygame doesn't have an event for this, so it must be a combination of
MOUSEBUTTONDOWN and MOUSEMOTION.
I've tried storing the positions of the previous frame with the get_pos() and subtracting the current frame's positions, but i can't figure out the next.
It accelerates way too fast..
I've also tried this with the get_rel() method of the mouse, with no success.
(Though i'm pretty sure i wasn't supposed to ++ the mouse position to the screen position)
I researched around to see if anyone else did this but i found only how to drag around something
on a fixed screen. I need the opposite - drag the screen around on a fixed grid.
So, if anyone has any idea or advice on how to make this mechanic, or any link that i could study about it, i would be grateful!
ps: I found something similar but it's written in JS and it's a pain to translate)
I got it working!
It still has some issues on the zoomIn/zoomOut additions but the main problem of dragging
the grid around is fixed.
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()

Resources