so I am working on a 2d game using pygame and PyopenGL, I have done some stuff to make it so the window is resizable. When the window resizes, the game resizes with it. This is all good.
My game is a tile based game with scrolling up and down.
However, the issue is, when I resize the screen, if I scroll up or down, strange lines appear between the tiles. This doesnt happen when scrolling left or right, which is odd. These lines only appear when at higher resolutions, on lower resolutions they do not exist. My code is rather large and in multiple files, so I will just post a link to it on github, but here is my window code with the resize function:
import numpy
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
from . import batch
from . import GAH
class window_handler:
def __init__(self,window_size=(640,360)):
self.window_size = window_size
self.main_window = pygame.display.set_mode(self.window_size, DOUBLEBUF|OPENGL|RESIZABLE)
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glViewport(0, 0, self.window_size[0], self.window_size[1])
self.projection_matrix = numpy.array((
(2/640, 0, 0, 0),
(0, -2/360, 0, 0),
(0, 0, 1, 0),
(0, 0, 0, 1)
), numpy.float32)
self.view_matrix = numpy.array((
(1, 0, 0, 0),
(0, 1, 0, 0),
(0, 0, 1, 0),
(-320, -180, 0, 1)
), numpy.float32)
self.view_projection_matrix = numpy.dot(self.view_matrix,self.projection_matrix)
batch.setup_shaders(vpMatrix = self.view_projection_matrix)
self.renderer = batch.Batch()
def rezise_request(self, event):
self.window_size = event.w,event.h
glViewport(0, 0, self.window_size[0], self.window_size[1])
def update_display(self):
pygame.display.flip()
def draw_game(self,draw):
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT)
self.renderer.begin()
for i in draw["background_0"]:
self.renderer.draw(self.graphics_handler["Background_images"][i[1]][0],i[0][0],i[0][1])
for i in draw["background_1"]:
self.renderer.draw(self.graphics_handler["Background_images"][i[1]][0],i[0][0],i[0][1])
for i in draw["background_2"]:
self.renderer.draw(self.graphics_handler["Background_images"][i[1]][0],i[0][0],i[0][1])
for i in draw["background_3"]:
self.renderer.draw(self.graphics_handler["Background_images"][i[1]][0],i[0][0],i[0][1])
for i in draw["tile_layer"]:
self.renderer.draw(self.graphics_handler["Tileset"]["tileset_"+str(i[1][1])][i[1][0]],i[0][0],i[0][1])
for i in draw["entity"].values():
if i[1] != None:
image = self.graphics_handler["Sprites"][i[1]][0]
self.renderer.draw(image,i[0][0],i[0][1],i[2])
self.renderer.end()
def quit(self):
self.renderer.delete()
Here is a a link to my code in full: github.com/EvelynMitchell199/Yumi
I figured out whats wrong with our code! So, basically when we set resolutions, if the resolution isn't one of the ones highlighted in green here, then everything scales weirdly creating lines between tiles. https://pacoup.com/2011/06/12/list-of-true-169-resolutions/
The resolution MUST be divisible by 8 otherwise the scaling between tiles will be different, and thus we can get lines between tiles.
Related
I'm trying to get the mouse coordinate on OpenGL scene.
My Code:
from PySide.QtGui import (QColor)
from PySide.QtCore import (Qt, QSize)
from PySide.QtOpenGL import (QGLWidget)
from OpenGL.GL import *
from OpenGL.GLU import *
class QGL(QGLWidget):
def __init__(self, parent=None):
self._pan_valid = False
super(QGL, self).__init__(parent)
self.setFocusPolicy(Qt.ClickFocus)
self.local_translate = (0.0, 0.0, 0.0)
self.zoomVal = 1.2
def minimumSizeHint(self):
return QSize(50, 50)
def sizeHint(self):
return QSize(800, 800)
def initializeGL(self):
self.qglClearColor(QColor.fromCmykF(0.0, 0.1, 0.0, 0.882))
glViewport( 0, 0, self.width(), self.height())
glEnable(GL_TEXTURE_2D)
glEnable(GL_CULL_FACE)
glEnable(GL_MULTISAMPLE)
glEnable(GL_LINE_SMOOTH, GL_LINE_WIDTH, GL_ALIASED_LINE_WIDTH_RANGE)
glShadeModel(GL_FLAT)
glEnable(GL_DEPTH_TEST)
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST)
glDepthRange (0.1, 1.0)
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
gluOrtho2D(-self.zoomVal, +self.zoomVal, -self.zoomVal, +self.zoomVal)
glLoadIdentity()
genList = glGenLists(1)
glNewList(genList, GL_COMPILE)
vertices = [
(0,0,0),
(0.5,0,0),
(0.5,0.5,0),
(0,0.5,0)
]
glBegin(GL_QUADS)
self.qglColor(QColor(0,255,255))
for vertex in vertices:
glVertex3fv(vertex)
glEnd()
glEndList()
glCallList(genList)
def mousePressEvent(self, event):
print event.pos()
print self.mapToGlobal(event.pos())
when I do this:
print event.pos()
it will give me the mouse position on the window.
and when I do this:
print self.mapToGlobal(event.pos())
it will give me the mouse position on the monitor.
How can I get the mouse position on the scene?
I'm using 2D viewport (gluOrtho2D).
I don't think there is a convenient built-in function for this, but it should be trivial to calculate, especially given that this is a 2D, orthographic scene. You know the size of the window in pixels, and you know where in that window the user clicked, given whatever event.pos() returns (also in pixels). What needs to happen then is that you need to map that range in pixels to your range specified in your gluOrtho2D call. The following code could be adapted as you see fit:
#specifying a bunch of example values
zoom_val = 1.2
window_size = (800, 600)
mouse_pos = (300, 150)
ortho_2d = (-zoom_val, +zoom_val, -zoom_val, +zoom_val)
#First, calculate the "normalized" mouse coordinates by dividing by window_size
mouse_norm_x = mouse_pos[0] / window_size[0]
mouse_norm_y = mouse_pos[1] / window_size[1]
#Now map those coordinates to your orthographic projection range
mouse_ortho_x = (mouse_norm_x * (ortho_2d[1] - ortho_2d[0])) + ortho_2d[0]
mouse_ortho_y = (mouse_norm_y * (ortho_2d[3] - ortho_2d[2])) + ortho_2d[2]
mouse_ortho = (mouse_ortho_x, mouse_ortho_y)
print(mouse_ortho)
Getting the z-coordinate is trickier. I would suggest reading up on the concept of "mouse-picking" for that. You would need to get the depth buffer, store its results in a texture, and sample the mouse coordinate at the appropriate location in that texture to get the z-coordinate. You can combine these two answers (1, 2) I have previously written for other questions together to get something working. Let me know if this helps!
I have two images, "desert" image over "winter" image:
import pyglet
desert_img = pyglet.image.load('assets/desert.jpg')
desert = pyglet.sprite.Sprite(desert_img, x=50, y=50)
winter_img = pyglet.image.load('assets/winter.jpg')
winter = pyglet.sprite.Sprite(winter_img, x=0, y=0)
window = pyglet.window.Window()
#window.event
def on_draw():
winter.draw()
desert.draw()
pyglet.app.run()
Result is:
I would like draw a square of "transparency" on desert image (winter image should be visible in this square). Is it possible ? How to do that ?
I found many question who permit to make transparency with image itself (png, alpha ... but no like i want).
Based on Torxed suggestion, replace image content with transparent bytes where we want to make it transparent:
import io
from PIL import Image
import pyglet
from PIL.PngImagePlugin import PngImageFile
def replace_content_with_transparency(img: PngImageFile, x, y, width, height):
pixels = img.load()
for i in range(x, width):
for j in range(y, height):
pixels[i, j] = (0, 0, 0, 0)
desert_png = Image.open('assets/desert.png')
replace_content_with_transparency(desert_png, 32, 32, 123, 123)
fake_file = io.BytesIO()
desert_png.save(fake_file, format='PNG')
desert_img = pyglet.image.load('img.png', file=fake_file)
desert = pyglet.sprite.Sprite(desert_img, x=50, y=50)
winter_img = pyglet.image.load('assets/winter.jpg')
winter = pyglet.sprite.Sprite(winter_img, x=0, y=0)
window = pyglet.window.Window()
#window.event
def on_draw():
winter.draw()
desert.draw()
pyglet.app.run()
I have some code that moves images left and right but I do not want them to appear on top of the right border, which I draw as a rectangle.
What are the options in Tkinter to keep a widget (in my example a rectangle) on top of some other widgets (in my code a tile, which is an image)?
I am drawing the rectangle and the image on one canvas.
I can image that using two canvas could do the trick, but are there any other options/settings?
Thanks
import Tkinter as tk # for Python2
import PIL.Image, PIL.ImageTk
win = tk.Tk()
#Create a canvas
canvas = tk.Canvas(win, height = 500, width = 500)
#Create a rectangle on the right of the canvas
rect = canvas.create_rectangle(250, 0, 500, 250, width = 2, fill = "red")
#Create an image
SPRITE = PIL.Image.open("sprite.png")
tilePIL = SPRITE.resize((100, 100))
tilePI = PIL.ImageTk.PhotoImage(tilePIL)
tile = canvas.create_image(100, 100, image = tilePI, tags = "a tag")
#Place the canvas
canvas.grid(row = 1, column = 0, rowspan = 5)
#Move the tile to the right.
#The tile will go on top of red rectangle. How to keep the rectangle on top of the tile?
canvas.coords(tile, (300, 100))
canvas.mainloop()
Use tag_raise() method:
canvas.tag_raise(tile)
Ive been trying to make a recursive rectangles and I wanted to make the rectangles move in the forward direction like each time it recursed, so that it gives a motion as one is going into an endless rectangles. Ive tried making the size bigger each time it recursed but failed as it wont recurse or nothing would show up. Any tips or how to do this would be appreciated. This sample I implemented from pygamearcade. I want to get the feeling as one is going into the rectangles and that can be implemented as the rectangles get bigger each time it goes through recurion. So any tips or how to do it is fine. Thank you
import pygame
# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
def recursive_draw(x, y, width, height):
""" Recursive rectangle function. """
pygame.draw.rect(screen, WHITE,
[x, y, width, height],
1)
speed = [10,0]
# Is the rectangle wide enough to draw again?
while (width > 14):
# Scale down
x += width * .1
y += height * .1
width *= .8
height *= .8
# Recursively draw again
recursive_draw(x, y, width, height)
pygame.init()
#rectanglelist = [big()]
# Set the height and width of the screen
size = [700, 500]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")
# Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# Set the screen background
screen.fill(BLACK)
# ALL CODE TO DRAW SHOULD GO BELOW THIS COMMENT
recursive_draw(0, 0, 700, 500)
# ALL CODE TO DRAW SHOULD GO ABOVE THIS COMMENT
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Limit to 60 frames per second
clock.tick(60)
# Be IDLE friendly. If you forget this line, the program will 'hang'
# on exit.
pygame.quit()
The problem is that your recursive_draw() function is not really a recursive function, because a recursive function is a function that conditionally calls itself:
Every properly designed recursive function must have at least one base case [A] and must redefine the problem into sub problems that work towards a base case [B].
def countdown(n):
if n < 1:
print "Lift Off" #[A]
else:
print n
countdown(n - 1) #[B]
What you could do for your code:
First fix the indent of your code.
Second, replace your while loop by an ´if´ statement.
This is the important part or recursion: The function calls itself unless a certain condition is not true anymore (in your case width > 14). For more information see How can I build a recursive function in python? or Recursion function in python on SO.
The updated function (code from http://www.balloonbuilding.com/ by Paul Vincent Craven):
def recursive_draw(x, y, width, height):
""" Recursive rectangle function. """
pygame.draw.rect(screen, BLACK, (x, y, width, height), 1)
# Is the rectangle wide enough to draw again?
if(width > 14):
# Scale down
x += width * .1
y += height * .1
width *= .8
height *= .8
# Recursively draw again
recursive_draw(x, y, width, height)
I hope this helps :)
EDIT:
The updated program:
import pygame
# Colors
BLUE = (55, 155, 255)
WHITE = (255, 255, 255)
def recursive_draw(x, y, width, height):
""" Recursive rectangle function. """
pygame.draw.rect(screen, WHITE, (x, y, width, height), 2)
# Is the rectangle wide enough to draw again?
if(width > 14):
# Scale down
x += width * .1
y += height * .1
width *= .8
height *= .8
# Recursively draw again
recursive_draw(x, y, width, height)
speed = [10,0]
pygame.init()
#rectanglelist = [big()]
# Set the height and width of the screen
size = [700, 500]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")
# Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# Set the screen background
screen.fill(BLUE)
# ALL CODE TO DRAW SHOULD GO BELOW THIS COMMENT
recursive_draw(0, 0, 700, 500)
# ALL CODE TO DRAW SHOULD GO ABOVE THIS COMMENT
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Limit to 60 frames per second
clock.tick(60)
# Be IDLE friendly. If you forget this line, the program will 'hang'
# on exit.
pygame.quit()
Screenshot:
So for a quick sample in pictures:
This is normal:
this is after rotating 180 deg on either the X or Y axis:
I don't see why this is happening at all. I'm using OpenTK to render a simple Bullet physics scene. The code is straight forward and it almost seems like there's something wrong in the way the matrix is handled. It's straight-forward render code:
GL.PushMatrix();
GL.MultMatrix(Body.MotionState.WorldTransform.ToArray());
GL.Scale(HalfX * 2, HalfY * 2, HalfZ * 2);
GL.Color4(Color4.Blue);
GL.Begin(PrimitiveType.Lines);
GL.Vertex3(0, 0, 0);
GL.Vertex3(0, 0, 10);
GL.End();
GL.Color4(Color4.Yellow);
GL.Begin(PrimitiveType.Lines);
GL.Vertex3(0, 0, 0);
GL.Vertex3(0, 10, 0);
GL.End();
GL.Color4(Color4.Green);
GL.Begin(PrimitiveType.Lines);
GL.Vertex3(0, 0, 0);
GL.Vertex3(10, 0, 0);
GL.End();
if (Body.ActivationState == ActivationState.ActiveTag)
{
GL.Color4(Color4.Blue);
}
else if (Mass == 0)
{
GL.Color4(Color4.Red);
}
else
{
GL.Color4(Color4.Green);
}
model.Draw();
GL.PopMatrix();
I've tried breaking it down to it's components: the translation vector is fine, scaling is fine, rotating on the Z axis appears fine... it's when you add rotations on the X or Y axis that it starts flying. I have console output going: the box is at exactly 6.9999 on the Z axis in both images.
Where am I going wrong? What am I missing? How do I fix this?!
Okay so... PushAttrib(AttribMask.AllAttribBits); PushMatrix(); in my /TEXTFONT LOADING CODE/ fixed it. Somehow, some weird attribute set in the code that loads fonts to later render made GL.Rotate rotate around (0, 0, 1) instead of (0, 0, 0)... OpenGL sure is temperamental...
So the lesson here is... never assume unrelated code is truly unrelated when dealing with OpenGL.