watching the awesome Spectrum Show, I thought about creating a retro game again.
At the moment, I'm trying to recreate the jump of Miner Willy (Manic Miner).
I took a look at how a "physics" jump would be emulated (in Pygame, I post my code below).
Then I thought, I better should experiment on the (emulated) Spectrum (using C), to find out, how the jump works in Spectrum pixels.
In BASIC, you can define UDGs and use "PRINT AT" to put them on the screen. I can do that in C too.
But Miner Willy doesn't just move in characters, he moves pixel-wise.
How on earth is this done?
The screen data is not even stored from top left to down right, like you would plot, but it's stored in these lines, that are shown, when a SREEN$-image is loaded.
"zx_pxy2saddr(x, y)" gives me a memory address. But there, a whole byte is stored. I then could alter a bit in this byte and plot, what I want that way, but that would be slow.
When I poke my whole UDG-bytes into these addresses, the UDG is shown quickly, but is moved only character-wise.
I assume, that "zx_pxy2saddr(50, 50)" and "zx_pxy2saddr(51, 50)" may give me the same memory address, if pixel 50/50 and pixel 51/50 are still in the same character-field.
That's all quite confusing (and frustrating to be honest). Could anybody tell me please, how pixel-wise movement is realized on the Spectrum?
----
Pygame-code for throw (or jump) movement:
Code: Select all
#!/usr/bin/python
# coding: utf-8
import pygame
from pygame.locals import *
import math
import os
G = 9.81
T = 0
DT = 0.017
class Ball:
def __init__(self, colour, position):
self.surface = pygame.Surface((20, 20))
pygame.draw.circle(self.surface, colour, (10, 10), 10)
self.rect = self.surface.get_rect()
self.x_prog = position[0]
self.y_prog = position[1]
self.rect.topleft = position
def initThrow(self, velocity, angle):
self.velocity = velocity
self.angle = angle
self.x_phys = 0
self.y_phys = 0
# The velocity is divided into its x and y-parts:
self.vy = self.velocity * math.sin(math.radians(self.angle))
self.vx = self.velocity * math.cos(math.radians(self.angle))
self.physToProg()
self.start = (self.x_prog, self.y_prog)
self.rect.topleft = self.start
def moveBall(self):
self.vy -= G * DT
self.x_phys += self.vx * DT
self.y_phys += self.vy * DT
self.physToProg()
self.rect.topleft = (self.x_prog, self.y_prog)
def physToProg(self):
self.x_prog = 400 * self.x_phys + 200
self.y_prog = -400 * self.y_phys + 300
def processEvents():
pygame.event.pump()
pressed = pygame.key.get_pressed()
if pressed[K_q] or pressed[K_ESCAPE]:
return "quit"
return 0
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
pygame.init()
os.environ['SDL_VIDEO_WINDOW_POS'] = "223, 39"
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption('Throw')
clock = pygame.time.Clock()
ball1 = Ball(RED, (100, 200))
vel = 3
ang = 60
ball1.initThrow(vel, ang)
ball2 = Ball(BLUE, (100, 200))
ball2.initThrow(vel, ang)
finishedtime = 0
while True:
clock.tick(20)
if processEvents() == "quit":
break
screen.fill(BLACK)
if ball1.rect.y <= ball1.start[1]:
ball1.moveBall()
T += DT
else:
finishedtime += 1
# clocktick 20:
if finishedtime > 20:
ball1.initThrow(vel, ang)
T = 0
finishedtime = 0
screen.blit(ball1.surface, ball1.rect)
screen.blit(ball2.surface, ball2.rect)
pygame.display.flip()