0

I was trying to implement splitscreen into my pygame slither.io clone by using how to create a 4-way split screen in pygame and for some reason only one of the screens works properly. (I used screens instead of rects, might be the issue)

All the code used for rendering is in:

  • PDraw(), used for centering the player on the screen
  • player.Render(), responible for drawing everything on the player's screen. Calls PDraw() for all objects and writes the player's position on the screen
  • for p in players: p.Render(), loops through all players and calls player.Render()

Google Drive: https://drive.google.com/drive/folders/1XxK2aakfWQT8ig58MMhoQ8Dt735UrsxM?usp=sharing

Code:

from email.quoprimime import body_check
from tkinter import Y
import pygame, time, math
import sys
from pygame.locals import QUIT

pygame.init() # initialises pygame
pygame.font.init() # initializes pygame.font

WIDTH = 592  # how wide the screen is
HEIGHT = 333  # how tall the screen is
CENTER = WIDTH / 2, HEIGHT / 2
screen = pygame.display.set_mode((WIDTH, HEIGHT))  # creates the screen and sets it's dimensions
pygame.display.set_caption('Snake') # changes the window name
gameFont = pygame.font.SysFont('Comic Sans MS', 30) # sets font and size of default text font

# https://stackoverflow.com/questions/20403675/how-to-create-a-4-way-split-screen-in-pygame
#p1_camera = pygame.Rect(0, 0, WIDTH / 2, HEIGHT / 2)
#p2_camera = pygame.Rect(WIDTH / 2, 0, WIDTH / 2, HEIGHT / 2)
#p3_camera = pygame.Rect(0, HEIGHT / 2, WIDTH / 2, HEIGHT / 2)
#p4_camera = pygame.Rect(WIDTH / 2, HEIGHT / 2, WIDTH / 2, HEIGHT / 2)

FPS = 60 # The maximum framerate the game can run at
prevTime = 0 # initializes prevTime variable
clock = pygame.time.Clock() # creates a game clock

players = [] # this list contains every player
clocks = [] # this list contains all deltaclocks
cams = [] # this list contains all cameras

# loads skins into the game
skin1 = pygame.image.load('pointycircle.png')
skin2 = pygame.image.load('1.png')
skin3 = pygame.image.load('blue.png')
skin4 = pygame.image.load('pika.png')


def WriteText(string, coordX, coordY, fontSize): # writes text on screen (from grepper search: how to display text on the screen pygame)
    #set the font to write with
    font = pygame.font.Font('freesansbold.ttf', fontSize) 
    #(0, 0, 0) is black, to make black text
    text = font.render(string, True, (0, 0, 0))
    #get the rect of the text
    textRect = text.get_rect()
    #set the position of the text
    textRect.center = (coordX, coordY)
    #add text to window
    screen.blit(text, textRect)
    #update window
    pygame.display.update()


class screenInstance: # class allows me to make new screens for splitscreen that I actually know how to use (bc I made it myself)

    def __init__(self, player): # initializes class
        self.player = player # what player is the focus of this camera?
        self.camLoc = len(cams) # how many cameras currently exist?
        print(self.camLoc) # debug
        if self.camLoc == 0: # if there are none
            self.corners = [(0, 0), (CENTER[0], 0), (0, CENTER[1]), CENTER] # the coordinates of the corners of the screen
            self.screen = pygame.Surface(CENTER) # make a new screen width the width and height of a quarter screen
            self.center = WIDTH / 4, HEIGHT / 4 # coordinates of the center of the screen so they can easily be used later
        if self.camLoc == 1: # if there is 1
            self.corners = [(CENTER[0], 0), (WIDTH, 0), CENTER, (WIDTH, CENTER[1])] # the coordinates of the corners of the screen
            self.screen = pygame.Surface(CENTER) # make a new screen width the width and height of a quarter screen
            self.center = WIDTH / 4 * 3, HEIGHT / 4 # coordinates of the center of the screen so they can easily be used later
        if self.camLoc == 2: # if there is 2
            self.corners = [(0, CENTER[1]), CENTER, (0, HEIGHT), (CENTER[0], HEIGHT)] # the coordinates of the corners of the screen
            self.screen = pygame.Surface(CENTER) # make a new screen width the width and height of a quarter screen
            self.center = WIDTH / 4, HEIGHT / 4 * 3 # coordinates of the center of the screen so they can easily be used later
        if self.camLoc == 3: # if there is 3
            self.corners = [CENTER, (WIDTH, CENTER[1]), (CENTER[0], HEIGHT), (WIDTH, HEIGHT)] # the coordinates of the corners of the screen
            self.screen = pygame.Surface(CENTER) # make a new screen width the width and height of a quarter screen
            self.center = WIDTH / 4 * 3, HEIGHT / 4 * 3 # coordinates of the center of the screen so they can easily be used later
        cams.append(self)

    def PDraw(self, img, position): # Perspective Draw: shifts things so that the player appears to be in the center and everything else is moving around it
        xPos = position[0] # gets the x of the inputted cooridinate
        xPlayerPos = self.player.position[0] # gets the player's x position
        xDrawPos = xPos - xPlayerPos # subtracts both to get the relative screen position

        yPos = position[1] # gets the y of the inputted cooridinate
        yPlayerPos = self.player.position[1] # gets the player's y position
        yDrawPos = yPos - yPlayerPos # subtracts both to get the relative screen position

        #if xDrawPos == 0 and yDrawPos == 0:
        #    print("Drawing " + str(self.player) + " at " + str(self.player.position)) # debug

        # draws the image at the relative position and centers it
        self.screen.blit(img, (xDrawPos + self.center[0] - img.get_width(), yDrawPos + self.center[1] - img.get_width()))


class player:  # handy code that made me remember classes exist -> https://stackoverflow.com/questions/59697004/top-down-movement-in-pygame

    def __init__(self, pos, w, h, dir, img):
        players.append(self) # adds itself to the players list
        self.x = pos[0]  # plater x position
        self.y = pos[1]  # player y position
        self.position = self.x, self.y # makes a variable containing both x and y
        self.w = w  # player width
        self.h = h  # player height
        self.size = (w + h) / 2 # gets the size of the player (width + height / 2)
        self.dir = dir # where is the player pointing
        self.originalImage = img  # the sprite of the player
        self.img = pygame.transform.scale(img, (self.w, self.h))  # the sprite of the player
        self.speed = 1700 / self.size # the equation for the speed is 1700 รท size
        print("Calculated Speed: " + str(self.speed)) # outputs the speed for debug
        self.xChange = 0 # initializes xChange
        self.yChange = self.speed # initializes yChange to speed to the player starts moving up on awake
        self.bodies = []
        self.bodyCount = 0 # how many peo- how many bodies are attached to the snake?
        self.maxBodies = 7 # how long can the snake get?
        self.cam = screenInstance(self)
        self.cloneTime = self.size / 250 # sets the amount of time that passes between the creation of new bodies
        self.cloneLoop = deltaClock(self.cloneTime, self.NewBody, True) # creates a looping deltaclock that makes a new body every x seconds

    def Motor(self): # changes position based on "change" variables and deltatime
        self.x -= self.xChange * dt
        self.y -= self.yChange * dt
        self.position = self.x, self.y

    def Rotate(self, angle): # https://gamedev.stackexchange.com/questions/132163/how-can-i-make-the-player-look-to-the-mouse-direction-pygame-2d
        #mouseX, mouseY = pygame.mouse.get_pos() # gets the mouse position (x, y)
        #relX, relY = mouseX - self.x, mouseY - self.y # gets the relative position to the player
        angle += 90 # calculates the angle to point towards the mouse and converts the result from radians to angles
        self.img = pygame.transform.scale(self.originalImage, (self.w, self.h)) # resets player sprite
        self.img = pygame.transform.rotate(self.img, float(angle-90)) # rotates the image
        self.rect = self.img.get_rect(center = self.position)

    def NewBody(self):
        self.bodies.append(body(self, 2)) # makes a new body object and puts it at the end of the bodies list
        self.bodyCount += 1 # when a new body is created, increase bodyCount by 1
        if self.bodyCount > self.maxBodies: # if the nember of bodies has exceeded the maximum length of the snake
            self.bodies[0].Kill() # kill the last body of the snake

    def Render(self):
        self.cam.screen.fill((255, 255, 255))
        for p in players: # runs for every player so all players are drawn on screen
            for b in p.bodies: # draws every body
                self.cam.PDraw(b.img, b.position)
            self.cam.PDraw(p.img, p.position) # draws the player after the bodies so it appears on top

        # https://stackoverflow.com/questions/20842801/how-to-display-text-in-pygame
        # writes text onto a new surface "textSurface" with the player's position, with no anti-aliasing, in black
        tSurface = gameFont.render((str(round(self.x)) + ", " + str(round(self.y))), False, (0, 0, 0))
        # copies the text into the splitscreen and draws it close to the top right corner of the splitscreen
        self.cam.screen.blit(tSurface, (self.cam.corners[0][0], self.cam.corners[0][1]))

        # draws the splitscreen onto the main screen
        screen.blit(self.cam.screen, self.cam.corners[0])
        print(str(self.cam.screen) + str(self.cam.corners[0]))

class body: # body class for easy body creation and destruction + local variables

    def __init__(self, player, time):
        self.player = player # who does this body belong to?
        self.position = self.player.position # sets the body position to the player position
        self.img = player.img # sets the body image to the current player image

    def Kill(self):
        self.player.bodies.remove(self) # removes the body from the bodies list
        del self # deletes this body

class deltaClock:

    def __init__(self, end, call, loop):
        clocks.append(self) # adds itself to the clocks list
        self.time = 0 # time starts at 0
        self.end = end # when will the timer end?
        self.call = call # what function should it call when it ends?
        self.loop = loop # will the timer loops when it ends?
    
    def Tick(self): # should be called every frame
        self.time += dt # adds deltatime to the time variable
        if self.time >= self.end: # if the timer is complete
            self.call() # calls the function inputted to the class
            if self.loop: # if the timer loops
                self.time = 0 # resets time to 0
            else: # otherwise delete this clock
                clocks.remove(self) # removes itself from the clocks list
                del self # kill instance


# makes a new instance of player (centered x2, size 25 x2, standing still x2, pointing in direction 0, looks like skinX)
p1 = player(CENTER, 25, 25, 0, skin1)
p2 = player((CENTER[0] + 10, CENTER[1] + 15*10), 25, 25, 0, skin2)
p3 = player((CENTER[0] + 20, CENTER[1] + 30*10), 25, 25, 0, skin3)
p4 = player((CENTER[0] + 30, CENTER[1] + 45*10), 25, 25, 0, skin4)

running = True
while running:  # runs until running is false

    # uses game clock to limit framerate to FPS (https://www.youtube.com/watch?v=XuyrHE6GIsc)
    clock.tick(FPS)

    # Get deltaTime. DeltaTime is the amount of times that has passed since the last frame, meaning that it is larger the less frames the game has. If all time-dependent variables are multiplied by deltaTime, it would mean that the game would run at the same speed regardless of the fps.
    now = time.time() # sets "now" to unix time ("what is unix time??????" - it's the amount of seconds that have passed since the beginning of 1970)
    if prevTime == 0: prevTime = time.time() - 0.015 # if prevTime has never been defined, make it an average interval away from "now" to deltaTime isn't set to unix time on the first frame
    dt = now - prevTime # calculates the amount of time that passed between frames by subtracting the unix time of last frame by the unix time of this frame
    prevTime = now # resets prevTime so it can be used in the next frame

    mousePos = pygame.mouse.get_pos() # gets the mouse position

    for event in pygame.event.get():  # repeats for every event that is recieved in this update
        # set running to false if event.quit is broadcasted (essentially quitting the game)
        if event.type == QUIT:
            running = False

        if event.type == pygame.KEYDOWN:  # whenever a key is pressed down

            # the game checks if any movement keys are pressed and if so, it increases the xChange or yChange variables respectively
            # these are used so that the player can move consistently until released without having to press the keys several times as key presses only run for 1 tick
            # https://stackoverflow.com/questions/59697004/top-down-movement-in-pygame

            # "and p1.xChange != -p1.speed" makes sure that the player can't turn directily into the opposite direction that it is currently moving in
            if event.key == pygame.K_a and p1.xChange != -p1.speed: # if key a
                p1.xChange = p1.speed      # move p1 left
                p1.yChange = 0             # stop all y movement
                p1.Rotate(90)              # makes the player look to the left
            if event.key == pygame.K_d and p1.xChange != p1.speed: # if key d
                p1.xChange = -p1.speed     # move p1 right
                p1.yChange = 0             # stop all y movement
                p1.Rotate(-90)             # makes the player look to the right
            if event.key == pygame.K_w and p1.yChange != -p1.speed: # if key w
                p1.yChange = p1.speed      # moves p1 up
                p1.xChange = 0             # stop all x movement
                p1.Rotate(0)               # makes the player look to the top
            if event.key == pygame.K_s and p1.yChange != p1.speed: # if key s
                p1.yChange = -p1.speed     # moves p1 down
                p1.xChange = 0             # stop all x movement
                p1.Rotate(180)             # makes the player look to the bottom

            if event.key == pygame.K_LEFT and p2.xChange != -p2.speed: # if key left arrow
                p2.xChange = p2.speed      # move p1 left
                p2.yChange = 0             # stop all y movement
                p2.Rotate(90)              # makes the player look to the left
            if event.key == pygame.K_RIGHT and p2.xChange != p2.speed: # if key right arrow
                p2.xChange = -p2.speed     # move p1 right
                p2.yChange = 0             # stop all y movement
                p2.Rotate(-90)             # makes the player look to the right
            if event.key == pygame.K_UP and p2.yChange != -p2.speed: # if key up arrow
                p2.yChange = p2.speed      # moves p1 up
                p2.xChange = 0             # stop all x movement
                p2.Rotate(0)               # makes the player look to the top
            if event.key == pygame.K_DOWN and p2.yChange != p2.speed: # if key down arrow
                p2.yChange = -p2.speed     # moves p1 down
                p2.xChange = 0             # stop all x movement
                p2.Rotate(180)             # makes the player look to the bottom
    
    # tick all players 
    for p in players:
        p.Motor() # update player positions based on x and yChange

    # ticks all deltaClocks
    for c in clocks:
        c.Tick()

    # Fill the background with white
    screen.fill((255, 255, 255))

    # draw players
    for p in players:
        p.Render()

    pygame.display.update() # updates the screen

# quit the game if 'running' is false
pygame.quit()
sys.exit()
Dmitriy Fialkovskiy
  • 3,065
  • 8
  • 32
  • 47
EEEE
  • 1
  • 1

0 Answers0