Closed arcade-mntra closed 12 months ago
Python was not tested for long before release. This should interest @blueloveTH .
This is expected. The old implementation is incorrect. Here is the stub file for the lastest tic-80.
def btn(id: int) -> bool: ...
def btnp(id: int, hold=-1, period=-1) -> bool: ...
def circ(x: int, y: int, radius: int, color: int): ...
def circb(x: int, y: int, radius: int, color: int): ...
def clip(x: int, y: int, width: int, height: int): ...
def cls(color=0): ...
def elli(x: int, y: int, a: int, b: int, color: int): ...
def ellib(x: int, y: int, a: int, b: int, color: int): ...
def exit(): ...
def fget(sprite_id: int, flag: int) -> bool: ...
def fset(sprite_id: int, flag: int, b: bool): ...
def font(text: str, x: int, y: int, chromakey: int, char_width=8, char_height=8, fixed=False, scale=1, alt=False) -> int: ...
def key(code=-1) -> bool: ...
def keyp(code=-1, hold=-1, period=-17) -> int: ...
def line(x0: int, y0: int, x1: int, y1: int, color: int): ...
def map(x=0, y=0, w=30, h=17, sx=0, sy=0, colorkey=-1, scale=1, remap=None): ...
def memcpy(dest: int, source: int, size: int): ...
def memset(dest: int, value: int, size: int): ...
def mget(x: int, y: int) -> int: ...
def mset(x: int, y: int, tile_id: int): ...
def mouse() -> tuple[int, int, bool, bool, bool, int, int]: ...
def music(track=-1, frame=-1, row=-1, loop=True, sustain=False, tempo=-1, speed=-1): ...
def peek(addr: int, bits=8) -> int: ...
def peek1(addr: int) -> int: ...
def peek2(addr: int) -> int: ...
def peek4(addr: int) -> int: ...
def pix(x: int, y: int, color: int=None) -> int | None: ...
def pmem(index: int, value: int=None) -> int: ...
def poke(addr: int, value: int, bits=8): ...
def poke1(addr: int, value: int): ...
def poke2(addr: int, value: int): ...
def poke4(addr: int, value: int): ...
def print(text, x=0, y=0, color=15, fixed=False, scale=1, alt=False): ...
def rect(x: int, y: int, w: int, h: int, color: int): ...
def rectb(x: int, y: int, w: int, h: int, color: int): ...
def reset(): ...
def sfx(id: int, note=-1, duration=-1, channel=0, volume=15, speed=0): ...
def spr(id: int, x: int, y: int, colorkey=-1, scale=1, flip=0, rotate=0, w=1, h=1): ...
def sync(mask=0, bank=0, tocart=False): ...
def ttri(x1: float, y1: float, x2: float, y2: float, x3: float, y3: float, u1: float, v1: float, u2: float, v2: float, u3: float, v3: float, texsrc=0, chromakey=-1, z1=0.0, z2=0.0, z3=0.0): ...
def time() -> int: ...
def trace(message, color=15): ...
def tri(x1: float, y1: float, x2: float, y2: float, x3: float, y3: float, color: int): ...
def trib(x1: float, y1: float, x2: float, y2: float, x3: float, y3: float, color: int): ...
def tstamp() -> int: ...
def vbank(bank: int=None) -> int: ...
Python allows implicit conversion from int
to float
but not float
to int
.
I've also checked tic-80's c api of spr
.
void tic_api_spr(tic_mem* memory, s32 index, s32 x, s32 y, s32 w, s32 h, u8* trans_colors, u8 trans_count, s32 scale, tic_flip flip, tic_rotate rotate)
It takes s32
a.k.a int
as arguments. So python interprets them as int
.
That project is too big to recreate here. The issue can be recreated by moving a sprite, incrementing its x or y position with a float value.
I know that you are relying on the previous float conversion and need to change a lot of things. For example,
spr(id, int(x), int(y))
or,
spr(id, round(x), round(y))
However, I believe not to do float
to int
implicit conversion is the right implementation and this is better for future :/
You can override the builtin functions to simply your work if you don't want to replace them all.
For example,
_spr = spr
spr = lambda id,x,y: _spr(id, int(x), int(y))
This is expected. The old implementation is incorrect.
@blueloveTH, thanks and congrats on developing pocketpy, it's like a dream come true using python in TIC80 :)
Is there a way to implement gradual acceleration, friction or angle rotation using only int values without everything running way too fast?
I've also checked tic-80's c api of
spr
.void tic_api_spr(tic_mem* memory, s32 index, s32 x, s32 y, s32 w, s32 h, u8* trans_colors, u8 trans_count, s32 scale, tic_flip flip, tic_rotate rotate)
It takes
s32
a.k.aint
as arguments. So python interprets them asint
.
But Lua implementation permits use of floats in spr() , circ(), rect() etc I ran Lua version of the above code using speed as int and as float, it didn't crash or raise an error.
Lua uses weak-typed numbers. Lua's number doesn't distinguish int
and float
(even string can be implicitly converted to numbers). Python is strong-typed. They are different.
Is there a way to implement gradual acceleration, friction or angle rotation using only int values without everything running way too fast?
I have little knowledge about this. But pocketpy has box2d
module support at here. This is not enabled for tic-80. You can enabled it in cmake files if you need this feature.
Is there a way to implement gradual acceleration, friction or angle rotation using only int values without everything running way too fast?
At the end the pixels positions are integers so you can still use floats but they will be converted to int at some point (but I may misunderstand what you mean). If you need to use floats (especially concerning angle rotation) you could use ttri instead.
Lua uses weak-typed numbers. Lua's number doesn't distinguish int and float (even string can be implicitly converted to numbers). Python is strong-typed. They are different.
Ah! That makes sense, Lua and its laissez faire approach.
But pocketpy has box2d module support at here. This is not enabled for tic-80. You can enabled it in cmake files if you need this feature.
Pocketpy has Box2d!? This is huge. Atm would be beyond my skillset if I run into bugs but am interested to give it a shot.
Is there a way to implement gradual acceleration, friction or angle rotation using only int values without everything running way too fast?
At the end the pixels positions are integers so you can still use floats but they will be converted to int at some point (but I may misunderstand what you mean). If you need to use floats (especially concerning angle rotation) you could use ttri instead.
When using Lua,
I can directly increment or decrement velocity
by acceleration = 0.25
per frame to apply the ramp-up effect.
Or multiply velocity
by friction = 0.95
i.e. 5% reduction per frame instead of stopping instantly to make the floor feel slippery.
I will try to implement it in python today. Meanwhile, sample Lua code to compare the two movements:
player={
x=120,
y=68,
x_vel=0,
max_speed=3,
acc=0.25,
img = 256
}
friction=0.95
function TIC()
cls()
--simple_movement()
advance_movement()
-- draw player
spr(player.img, player.x, player.y)
print(player.x)
end
function simple_movement()
if btn(2) then
player.x = player.x - player.max_speed --LEFT
elseif btn(3) then
player.x = player.x + player.max_speed --RIGHT
end
end
function advance_movement()
-- apply friction when btn released
player.x_vel = player.x_vel * friction
-- apply acc when moving Left or Right btn pressed
if btn(2) then
player.x_vel = player.x_vel - player.acc --LEFT
elseif btn(3) then
player.x_vel = player.x_vel + player.acc --RIGHT
end
--limit left/right max speed
if player.x_vel < -player.max_speed then
player.x_vel = -player.max_speed
end
if player.x_vel > player.max_speed then
player.x_vel = player.max_speed
end
--apply x_vel to player position
player.x = player.x + player.x_vel
end
emm..box2d is much more lightweight than pocketpy.
I'm currently working on a making course using game engines like Godot, Defold and beginner python gamedev course using PygameZero(pygame minus the boilerplate). So crushed for time.
Just last month discovered TIC80 has python and performs way better than pygame.
It's a pain to export to web a game made in pygame, which is where TIC80 shines with ease of export to web, desktop etc.
TIC80 has integrated sprite editor, tilemap editor, sfx editor and code editor that works on mobile phones also. This is huge for teaching programming in developing countries where kids dont have laptops but have access to mobile phones.
If TIC80 can ship with box2d enabled it has potential to become the default tool to teach python in schools and make HTML5 games instead of the boilerplate roadblocks the Pygame puts you through.
pocketpy integrated box2d a long time ago. box2d
module is stable. And it is used for my custom engine.
To enable it, just add a line into cmakelists.txt,
option(PK_USE_BOX2D "" ON)
then you can import box2d
.
tic-80's pocketpy does not enable box2d by default. Because I think tic-80 aims to restrict users, making them not too powerful.
Following up on the simple movement vs smooth movement situation,
Converting float values to nearest whole number before using the spr() function results in janky movement as seen in the gif below.
Using, spr(id, round(x), round(y)) works but @blueloveTH has cautioned not to implicitly convert float to int inside the spr() function for potential future compatibility reasons.
Using his following solution of custom spr implementation solves the problem for achieving smooth/gradual movement.
_spr = spr spr = lambda id,x,y: _spr(id, int(x), int(y))
Python code to comapre simple movement vs smooth movement.
## -- HELPER FUNCTION -- ##
_spr = spr
spr = lambda id,x,y: _spr(id, int(x), int(y))
class Box:
def __init__(self,x,y,id):
self.x = x
self.y = y
self.x_vel = 0
self.x_dir = 1
self.max_speed = 3
self.acc = 0.25
self.friction = 0.95
self.id = id
box_1 = Box(120,24,256)
box_2 = Box(120,64,257)
box_3 = Box(120,104,258)
def move(object, mode):
# simple movment, instant start and stop
if mode == 1:
# L-R movement
if btn(2): object.x -= object.max_speed
if btn(3): object.x += object.max_speed
# advance movement with acc and friction
if mode > 1:
# apply friction to velocity every frame
object.x_vel *= object.friction
# L-R movement, apply acc to velocity when btn pressed
if btn(2): object.x_vel -= object.acc
if btn(3): object.x_vel += object.acc
# apply velocity to x position every frame
object.x += object.x_vel
# rounding float to nearest whole number
# gives smooth start but janky stop
if mode == 2: object.x = round(object.x)
# rounding float to nearst decimal place
# gives smooth start and fairly smooth stop
if mode == 3: object.x = round(object.x,2)
# draw box
#spr(object.id, round(object.x), round(object.y)) #this works but can cause future compatibility issue
spr(object.id, object.x, object.y) #using custom implementation of spr
print("x: "+str(object.x),int(object.x-4),int(object.y-8))
def TIC():
cls(0)
# mode = 1: simple movement, no acc, no friction
# mode = 2: movement with acc, friction and round(box.x)
# mode = 3: movement with acc, friction and round(box.x,1)
move(box_1,1)
move(box_2,2)
move(box_3,3)
# DISPLAY INFO
print("STOPPAGE", 2,2,12)
print("Instant:", 2,24,3)
print("Janky:", 2,64,5)
print("Gradual:", 2,104,10)
This is really impressing!
Some tips:
print("x: "+str(object.x),int(object.x-4),int(object.y-8))
You can use f-string to format floats, which avoids 0.9999999...
print(f"x: {object.x:.2f}",int(object.x-4),int(object.y-8))
You can use f-string to format floats, which avoids 0.9999999...
Fantastic. pocketpy keeps delivering. I have now bookmarked the cheat-sheet for quick reference :)
Does pocketpy have SimpleNamespace ?
That would be super convenient to make small data containers with dot access to attributes instead of using a Dict which is relatively more cumbersome to use.
pocketpy supports standard python modules with __init__.py
. But you need a filesystem (not necessary to use real filesystem, virtual interfaces are also ok)
I don't think tic-80 have any support of this.
No problem. I'm enjoying using python in Tic-80. Will upload some simple games in the coming weeks. Thanks for the help guys.
A python project made with previous version crashed when run using current version with an error expecting int values in spr(id, x, y). Some objects like projectiles had advance movement with acceleration as a float or enemies with variable speed in increments of 0.5.
That project is too big to recreate here. The issue can be recreated by moving a sprite, incrementing its x or y position with a float value.
Similar error noticed for other builtin draw shape functions like pix(), circ(), rect() etc.
Version : 1.1.2837 (be42d6f) OS : Windows 10
Reproduction steps
speed = 1
and crashes ifspeed = 1.0
`x = 120 y = 24 w = 8 h = 8 id = 256 speed = 1.0
def TIC():
`