Forum Archive

Flappy bird

Karina

Hello everyone! I'm writing the game and at the beginning now

Karina
from scene import *
import random


class Column(SpriteNode):
    def __init__(self, **kwargs):
        SpriteNode.__init__(self, 'plf:Tile_BoxCrate_double', **kwargs)


class Game(Scene):
    def setup(self):
        self.background_color = '#99d7ff'
        x = 0
        ground = Node(parent=self)
        while x < self.size.w:
            lower_tile = SpriteNode('plf:Ground_Stone', (x, 30))
            higher_tile = SpriteNode('plf:Ground_Stone', (x, 738))

            x += 60
            ground.add_child(lower_tile)
            ground.add_child(higher_tile)
            self.add_column()


    def add_column(self):
        lower = random.randint(0, 360) // 64
        higher = random.randint(0, 360) // 64
        y = 45
        for i in range(lower):
            column = Column(parent=self)
            column.anchor_point = (0.5, 0)
            column.position = (self.size.w, y)
            y += 64
        y = 738
        for i in range(higher):
            column = Column(parent=self)
            column.anchor_point = (0.5, column.size.h)
            column.position = (self.size.w, y)
            y -= 64


run(Game())

This only makes ground and two columns at the end of the screen. The problem is that whenever I run this, the columns always at the same size. But I use random here, so they should get different each time. I can't get why is it so

stephen

Hello @Karina

im going to look into your code and ill br back with somthing for you. i do first want to suggest that instead of Random int you create Brushes kind of like Minecraft did for Trees. you would creat a class to for your brush objects then place them in a list. then use Random Choice instead and this randomly selects from list. gives you control of the objectvdesigns and easier to manage i find than dealing with ranges. that ssid ill be back β˜ΊοΈπŸ˜‰πŸ€“

Karina

I am new in programming and this is my first program in scene, so I don't know what are theese things. But thank you anyway

Drizzel

@Karina the issue lies in line 22.
Since add_column is in the loop, it will get called plenty of times. This results in a lot of columns stacked on top of another, and it is very probable that at least once these columns has the maximum number boxes, and therefore the β€žsmallestβ€œ gap.
Just remove one of the indents in line 22 to fix this.

Btw, what @stephen said is certainly worth looking into, like just about any of his suggestions

stephen

@Karina

heres is example of using brush like objects with scrolling nd random (not too random) spacing. if you have ny questions please ask!

@Drizzel thank you, i alwys try to help out tht way i get to learn kong the wy aswell πŸ˜ŽπŸ€“πŸ€“


from scene import *
import sound
import random
import math

A = Action
def w(): return get_screen_size()[0]
def h(): return get_screen_size()[1]
def MaxBrushSize(): return 10-2

def BaseBlock(parent):
    return SpriteNode(Texture('plf:Tile_BoxItem_boxed'),
                        size=Size(64, 64), parent=parent,
                        anchor_point=(0.0, 0.0))

def Block(parent):
    return SpriteNode(Texture('plf:Tile_BoxCrate_double'), 
                        size=Size(64, 64), parent=parent,
                        anchor_point=(0.0, 0.0))

def Stone(parent, x):
    return SpriteNode(Texture('plf:Tile_BrickGrey'),
                        size=Size(64, 64), parent=parent,
                        anchor_point=(0.0, 0.0), position=Point(x*64, 0))

class TopBrush(Node):
    def __init__(self, brushSize, *args, **kwargs):
        self.base=BaseBlock(self)
        self.size=Size(64, 64)
        self.position=Point(w()+(self.size.w), h()-self.size.h)
        self.brushSize=brushSize
        self.blocks=list([self.base, ])

        for x in range(1, self.brushSize):
            b=Block(self)
            b.position=(self.base.position[0], self.base.position[1] - x*b.size[1])
            self.blocks.append(b)

class BottomBrush(Node):
    def __init__(self, brushSize, *args, **kwargs):
        self.base=BaseBlock(self)
        self.size=Size(64, 64)
        self.position=Point(w()+(self.size.w), 0)
        self.brushSize=brushSize
        self.blocks=list([self.base, ])

        for x in range(1, self.brushSize):
            b=Block(self)
            b.position=(self.base.position[0], self.base.position[1] + x*b.size[1])
            self.blocks.append(b)





class MyScene (Scene):
    def setup(self):
        self.background=SpriteNode(Texture('plf:BG_Colored_grass'), 
            size=Size(w(), h()), position=self.size/2, parent=self)
        self.preset=[(1, 7),(2, 6),(3, 5),(4, 4),(5, 3),(6, 2),(7, 1)]
        self.i=0.0
        self.brushSpawnTimer=3.0
        self.brushes=list([])
        self.scrollSpeed=40

        for x in range(int((w()*128)/64)):
            self.brushes.append(Stone(self, x))



    def Add_Brush(self, choice):
        if self.i > self.brushSpawnTimer:
            bb=BottomBrush(choice[0])
            tb=TopBrush(choice[1])
            self.add_child(bb)
            self.add_child(tb)
            self.brushes.append(bb)
            self.brushes.append(tb)
            self.i=0.0
            self.brushSpawnTimer = random.randrange(3, 6)
        else:
            sb=Stone(self, (w()+128)/64)
            self.brushes.append(sb)


    def Remove_Brush(self, brush):
        self.brushes.remove(brush)
        brush.remove_from_parent()
        self.Add_Brush(random.choice(self.preset))


    def did_change_size(self):
        pass

    def Scroll(self, brush):
        x=brush.position[0]-self.scrollSpeed*self.dt
        y=brush.position[1]
        return Point(x, y)

    def update(self):
        self.i += self.dt
        for brush in self.brushes:
            if brush.position[0] <= -64:
                self.Remove_Brush(brush)
            brush.position = self.Scroll(brush)

    def touch_began(self, touch):
        pass

    def touch_moved(self, touch):
        pass

    def touch_ended(self, touch):
        pass

if __name__ == '__main__':
    run(MyScene(), show_fps=False)

stephen

Quick Note

The example i provided is for landscpe if you run portrait the gapps will not adjust. this can be handled by checking orientation and swapping the preset lists accordingly

Karina

@stephen I don't understand to much things, I've just read the tutorial and tried to do my own game.
I'll try to get it with time

stephen

@Karina il give a bit of a run down just give me few min to write it ☺️

Karina

@Drizzel yes I got it. Now it works how it should

Drizzel

@Karina

This is an old and sometimes very poorly written clone I did to learn about ai (flappy bird is an awesome game for basic artificial intelligence experiments). I didn’t bother about graphics, but maybe it’s helpful to you.

Run this to play the game:

from scene import *
import sound
import random
import math
import gameExtension as extension
#import nnExtension as nn
A = Action

class MyScene (Scene):

    def setup(self):
        self.background_color = 'beige'
        self.gravity = Vector2(0, -self.size.y/1500)
        self.birds = []

        self.running = True
        extension.setup_walls(self)
        extension.update_walls(self)

        #self.birdCount = 1 #uncomment if using ai

        for x in range(self.birdCount):
            extension.add_bird(self)
        pass

    def did_change_size(self):
        pass

    def update(self):
        if self.running:
            self.running = False
            extension.update_walls(self)

            for bird in self.birds:
                if bird.distance >= 10000: bird.dead = True
                if not bird.dead:
                    self.running = True
                    extension.count_distance(self, bird)
                    extension.update_bird(self, bird)
                    bird.data = extension.get_data(self, bird)

                    if bird.position.y-bird.size.y/2 <= 0 or bird.position.y+bird.size.y/2 >= self.size.y:
                        bird.color = 'red'
                        bird.dead = True

                    for wall in self.walls:
                        if bird.frame.intersects(wall.frame):
                            bird.color = 'red'
                            bird.dead = True
                else:
                    if bird.position.x + bird.size.x/2 >= 0:
                        bird.position = (bird.position.x - 1, bird.position.y)



    def touch_began(self, touch):
        self.birds[0].up = True
        pass

    def touch_moved(self, touch):
        pass

    def touch_ended(self, touch):
        pass

if __name__ == '__main__':
    run(MyScene(), PORTRAIT, show_fps=True)

Save this in the same folder as the upper main code as gameExtension.py

from scene import *
import random

def add_bird(self):
    self.birds.append(ShapeNode(rect(0,0,10,10)))
    bird = self.birds[len(self.birds)-1]
    bird.color = 'black'
    bird.size = (self.size.x/50, self.size.x/50)
    bird.position = (self.size.x/4, self.size.y/2)
    bird.z_position = 2
    bird.dead = False
    bird.distance = 0
    bird.up = False
    bird.max_fall_vel = Vector2(0, self.size.y/100)
    bird.up_acc = Vector2(0, self.size.y/2)
    bird.vel = Vector2(0, 0)
    bird.acc = Vector2(0, 0)
    bird.data = get_data(self, bird)
    self.add_child(bird)

def setup_walls(self):
    self.wall_distance = self.size.x/4
    self.gap_size = self.size.y/6
    self.wall_width = self.size.x/14
    self.distance_to_next_wall = self.wall_distance + 1
    self.walls = []

def count_distance(self, bird):
    bird.distance = bird.distance + 1

def update_walls(self):
    if self.distance_to_next_wall >= self.wall_distance:
        self.distance_to_next_wall = 0
        build_wall(self)
    else: self.distance_to_next_wall = self.distance_to_next_wall+1

    removal = []
    for x in range(len(self.walls)):
        wall = self.walls[x]
        wall.position = (wall.position.x - 1, wall.position.y)

        if wall.position.x + wall.size.x/2 < 0:
            removal.append(x)

    for x in range(len(removal)):
        wallIndex = removal[x]-x
        wall = self.walls[wallIndex]
        wall.remove_from_parent()
        self.walls.pop(wallIndex)

def build_wall(self):
    posY = random.randint(round(self.gap_size/2), round(self.size.y - self.gap_size/2)) #random position of gap

    self.walls.append(ShapeNode(rect(0,0,10,10))) #set up the upper wall
    wall = self.walls[len(self.walls)-1]
    wall.size = (self.wall_width, self.size.y - posY - self.gap_size/2)
    wall.color = 'grey'
    wall.position = (self.size.x + self.wall_width/2, self.size.y - wall.size.y/2)
    wall.z_position =  1
    self.add_child(wall)

    self.walls.append(ShapeNode(rect(0,0,10,10))) #set up the lower wall
    wall = self.walls[len(self.walls)-1]
    wall.size = (self.wall_width, posY - self.gap_size/2)
    wall.color = 'grey'
    wall.position = (self.size.x + self.wall_width/2, wall.size.y/2)
    wall.z_position =  1
    self.add_child(wall)

def update_bird(self, bird):
    if bird.up:
        bird.up = False
        bird.acc = bird.up_acc
    else: bird.acc = self.gravity

    bird.vel = bird.vel + bird.acc
    if bird.vel[1] > bird.max_fall_vel[1]: bird.vel = bird.max_fall_vel


    bird.position = bird.position + bird.vel

def get_data(self, bird):
    data = []
    for x in range(len(self.walls)):
        wall = self.walls[x]
        if wall.position.x + wall.size.x/2 >= bird.position.x - bird.size.x/2:
            y_to_gap = (wall.position.x - wall.size.x/2) - (bird.position.x + bird.size.x/2)
            if y_to_gap < 0: y_to_gap = 0
            data.append(y_to_gap)
            x_to_upper_wall = (wall.position.y - wall.size.y/2) - (bird.position.y + bird.size.y/2)
            data.append(x_to_upper_wall)
            wall = self.walls[x+1]
            x_to_lower_wall = (wall.position.y + wall.size.y/2) - (bird.position.y - bird.size.y/2)
            data.append(x_to_lower_wall)
            break
    distance_to_top = self.size.y - bird.position.y + bird.size.y/2
    data.append(distance_to_top)
    distance_to_bottom = bird.position.y - bird.size.y/2
    data.append(distance_to_bottom)
    velocity = bird.vel[1]
    data.append(velocity)
    return data
stephen

@Karina
i hope this helps..

First

we have A = Action This is simply convenience. Actions are the scene module's Nimation system. You dont have to use this. As shown in the example you can do all manual animation inside the Scene.update() method. called aprox. 60/sec Read more on actions in the Documentation for Actions

Second

def w(): return get_screen_size()[0]
def h(): return get_screen_size()[1]
def MaxBrushSize(): return 10-2

scene doent handle Global variables very well, so im told, o to avoid thi we have some "Global" variable function. all the do is return the value. vlue here is from
get_screen_size() β‡’ Tuple(float, float)
the float values is our scren size in Points. More about Points here
MaxBrushSize is just in case we needed a 'clamp' for Arithmatic functions.

Third


def BaseBlock(parent):
    return SpriteNode(Texture('plf:Tile_BoxItem_boxed'),
                        size=Size(64, 64), parent=parent,
                        anchor_point=(0.0, 0.0))

def Block(parent):
    return SpriteNode(Texture('plf:Tile_BoxCrate_double'), 
                        size=Size(64, 64), parent=parent,
                        anchor_point=(0.0, 0.0))

def Stone(parent, x):
    return SpriteNode(Texture('plf:Tile_BrickGrey'),
                        size=Size(64, 64), parent=parent,
                        anchor_point=(0.0, 0.0), position=Point(x*64, 0))

Functions returning Our Sprites. By doing this we clean up our code making it eaier to read and debug. Technically we coud of done this in one function and check the string input for "BrickGrey" to add the minor position change. I split them up o help understanding whats going on fr yourself.

Next

class TopBrush(Node):
    def __init__(self, brushSize, *args, **kwargs):
        self.base=BaseBlock(self)
        self.size=Size(64, 64)
        self.position=Point(w()+(self.size.w), h()-self.size.h)
        self.brushSize=brushSize
        self.blocks=list([self.base, ])

        for x in range(1, self.brushSize):
            b=Block(self)
            b.position=(self.base.position[0], self.base.position[1] - x*b.size[1])
            self.blocks.append(b)

class BottomBrush(Node):
    def __init__(self, brushSize, *args, **kwargs):
        self.base=BaseBlock(self)
        self.size=Size(64, 64)
        self.position=Point(w()+(self.size.w), 0)
        self.brushSize=brushSize
        self.blocks=list([self.base, ])

        for x in range(1, self.brushSize):
            b=Block(self)
            b.position=(self.base.position[0], self.base.position[1] + x*b.size[1])
            self.blocks.append(b)

Pretyslf explnitoy here. simply grouping our sprits into two secions and setting thier position for pillar like setup. TopBrush and BottomBrush. Nothing els speceal here.

Lastly

Now that we have everything prepared we can play with it ☺️.

class MyScene (Scene):
    def setup(self):
        self.background=SpriteNode(Texture('plf:BG_Colored_grass'), 
            size=Size(w(), h()), position=self.size/2, parent=self)
        self.preset=[(1, 7),(2, 6),(3, 5),(4, 4),(5, 3),(6, 2),(7, 1)]
        self.i=0.0
        self.brushSpawnTimer=3.0
        self.brushes=list([])
        self.scrollSpeed=40

        for x in range(int((w()*128)/64)):
            self.brushes.append(Stone(self, x))

setup() is called after the Scene object is returned bu before Event Loop Starts so here you can use Scene Variables such as size or bounds to setup your Game screen Invironment.
*Note: you can overide the __init__ method but you must call to Scene.__init__(self, *args, **kwargs) otherwise your loop will become unstable and crash. generally no reason to call it. setup() usualy is all u need .

    def Add_Brush(self, choice):
        if self.i > self.brushSpawnTimer:
            bb=BottomBrush(choice[0])
            tb=TopBrush(choice[1])
            self.add_child(bb)
            self.add_child(tb)
            self.brushes.append(bb)
            self.brushes.append(tb)
            self.i=0.0
            self.brushSpawnTimer = random.randrange(3, 6)
        else:
            sb=Stone(self, (w()+128)/64)
            self.brushes.append(sb)


    def Remove_Brush(self, brush):
        self.brushes.remove(brush)
        brush.remove_from_parent()
        self.Add_Brush(random.choice(self.preset))

Methods Add_Brush and Remove_Brush are very important. there would be no level without Add lol and Without Remove we could build up memory usage and thats never good. always clean up after yourself πŸ˜¬πŸ™ƒ

    def Scroll(self, brush):
        x=brush.position[0]-self.scrollSpeed*self.dt
        y=brush.position[1]
        return Point(x, y)

    def update(self):
        self.i += self.dt
        for brush in self.brushes:
            if brush.position[0] <= -64:
                self.Remove_Brush(brush)
            brush.position = self.Scroll(brush)

Finaly our Scroll and update methods.. these two bring it all to life.. Only thing to realy spotlight here for you is self.dt
this is the Time Delta, time between calls. in this case its the time between Frames and usually 0.00165.... We are using it for two uses. one is a timer for spawning our brushes and other is to smooth out the movment of our objects to make it visually appealing..


I hope thi helps you understand hts going on. Scene is a powerful tool and not just for game design but you have a long frustrating jurny ahead of you.. but its worth it β˜ΊοΈπŸ€“πŸ€“πŸ€“ i would suggest you look into using scene and ui modules togeather.you only need to call fom scene import * and ui is already imported in scene. ready or you. if ou do this you get a much more devloper freindly User interface along with your game loop. all you need to do is connect your Scene class to a scene.SceneView and add it to your CustomView with self.add_subview().

Ill be glad to help with anything else you need.

stephen

@Drizzel well done πŸ™ƒπŸ€“πŸ˜Ž

Drizzel

@stephen thanks for the kind words, but some things are clumsy at best. Your summary is awesome, though. Although I am really glad that I can help here :) That way I can return at least a small amount of the support this community here has given me

Karina

@Drizzel you say you clumsy, but I don't know how to do theese things at all)
your explanations helped to understand the beginnin, I think it'll take time to understand it all
Do you know anything like Clock in pygame to work with time? I need to make a new column every 10 secs for example

ccc

If you need inspiration, https://github.com/Pythonista-Tools/Pythonista-Tools/blob/master/Games.md features a Flappy Bird style game called Jumpy Octopus.

Karina

@Drizzel i see now what brushes are. Do you know some article to read about it? But what difference between w() and self.size.w?
And MaxBrushSize() just returns 8?
And what difference between creating Potint and just passing a tuple to the position argument?
Sorry about bombarding you with questions)

Drizzel

@Karina said:

@Drizzel i see now what brushes are. Do you know some article to read about it? But what difference between w() and self.size.w?
And MaxBrushSize() just returns 8?
And what difference between creating Potint and just passing a tuple to the position argument?
Sorry about bombarding you with questions)

I'll just forward that to @stephen for now (I'm rather pressed on time for the next few hours), he's the mastermind behind brushes.

Karina

@stephen thank you for help and can you give me some articles about brushes and games on pythonista?
And do you know a way to work with time like in pygame.Clock? I already made a column move and need to add column every 5 seconds

Drizzel

@Karina said:

@Drizzel you say you clumsy, but I don't know how to do theese things at all)
your explanations helped to understand the beginnin, I think it'll take time to understand it all
Do you know anything like Clock in pygame to work with time? I need to make a new column every 10 secs for example

The update(...) function (is used in my code and the code by @stephen) is executed 60 times a second (as long as your device can handle the load). Here's a good explanation. You can use self.dt to check how much time has passed since the last frame was calculated, and self.t to check for how long the scene has been running.
Then just do put this line into setup(...):

self.time_of_last_column = self.t

and merge this with your update(self):

def update(self):
    if self.t - self.time_of_last_column >= 10:
        #put the code to place a new column here
        self.time_of_last_column = self.t
Karina

@Drizzel yes I have the docs in pythonista

I tried to do it like that, but I only get the black screen that I can't even close

def update(self):
        time_passed = 0
        while True:
            time_passed += self.dt
            if time_passed >= 0.5:
                print(time_passed)
                self.add_column()
                time_passed = 0
        self.column_moves()
Drizzel

You have to remove the loop, like this:

def update(self):
        time_passed += self.dt
        if time_passed >= 0.5:
            print(time_passed)
            self.add_column()
            time_passed = 0
            self.column_moves()

Everything inside update() gets executed 60 times per second. Just put self.time_passed = 0 in setup()

Drizzel

This is a bit smarter:

from scene import *
import sound
import random
import math
A = Action

class MyScene (Scene):
    def setup(self):
        self.background_color = 'white'

        self.blocks = []
        for x in range(5):
            block = ShapeNode(rect(0,0,10,10))
            block.color = 'black'
            block.size = (30, 30)
            block.position = (self.size.x - 30, x*block.size[1] + block.size[1]/2)
            self.add_child(block)
            self.blocks.append(block)

        self.previous_time = self.t #self.t is the time that the game has been running

    def update(self): #everything in Here gets called 60 times a second
        if self.t - self.previous_time >= .5: 
            self.previous_time = self.t
            self.move_blocks()

    def move_blocks(self):
        for block in self.blocks:
            position = block.position
            new_x = position[0] - 10
            block.position = (new_x, position[1])

if __name__ == '__main__':
    run(MyScene(), show_fps=False)
Karina

@stephen about your brushes I began to implement it by I don 't understand some things:
what the difference between w() and self.size.w and
MaxBrushSize just returns 8??
And the program and the graphics look great😊

Karina

```
from scene import *
import random
import time

A = Action()

def Ground(parent):
return SpriteNode('plf:Ground_Grass', parent=parent)

def ColumnBlock(parent):
return SpriteNode('plc:Brown_Block', parent=parent)

class BottomBrush(Node):
def init(self):
self.ground = GroundBlock(self)
self.position = (self.size.w)

class Game(Scene):
def setup(self):
self.background_color = '#99d7ff'
#to track when 5 seconds passed and need to add a new column
self.time_passed = 0
#add the first column so you don't have to wait for it 5 seconds
self.columns = []
self.add_column()
x = 0
ground = Node(parent=self)
ground.z_position = -1
#building the upper and lower ground
while x < self.size.w:
lower_tile = SpriteNode('plf:Ground_Grass', (x, 30))
higher_tile = SpriteNode('plf:Ground_GrassCenter', (x, 738))
x += 60
ground.add_child(lower_tile)
ground.add_child(higher_tile)

    self.speed = 1


def update(self):
    self.time_passed += self.dt
    if self.time_passed >= 5:
        self.add_column()
        self.time_passed = 0
    self.column_moves()


def add_column(self):
    lower = random.randint(0, 360) // 64
    higher = 9 - lower
    #building the lower part
    y = 35
    for i in range(lower):
        block = ColumnBlock(parent=self)
        block.anchor_point = (0.5, 0)
        block.position = (self.size.w, y)
        self.columns.append(block)
        y += 64
    #building the higher part
    y = 738
    for i in range(higher):
        block = ColumnBlock(parent=self)
        block.anchor_point = (0.5, block.size.h)
        block.position = (self.size.w, y)
        self.columns.append(block)
        y -= 64


def column_moves(self):
    actions = [A.move_by(-self.size.w, 0, 30/self.speed), A.remove()]
    for i in self.columns:
        i.run_action(A.sequence(actions))

run(Game())```

For now I have this. It makes columns move just like I need. There will be the bird and the functions it needs, and I'm doneπŸ˜€

stephen

@Karina

Outstanding work so far! im very impressed on how fast you are grasping this.

first ill answer your questions πŸ€“


what the difference between w() and self.size.w?

In my example w() is recieving the width size of the screen directly. self.size.w is reference to the current Node's width. Yes when used inside your Subclass of scene.Scene this will be the same value. but this is only becasue you are initiating its View by using the module level run() function. (run always launches in fullscreen). but in the future you may notwant fullscreen and this means you would use a custom ui.View class then add a SceneView to it. now if you use the w() function you will get screen width as expected but now your self.size.w will return a different value referencing the SceneView's frame (frame ⇨ Tuple(x, y, width, height)) you can also retrieve this uing MySceneView.frame[2]. in our case i didnt needto use the w() function but we practice how we wantto playon game day. 😎 good habets produce great programs.


### MaxBrushSize just returns 8??

yes i know the return 10-2 seems useless. but maybe down the road you want to create, lets say, powerups. one might be the gaps get larger bfor n seconds. since we already have this in out code all we need to do is make a quick change.. this would be the replacement β₯₯

    def MaxBrushSize(mod):
        return 10 - mod

then somwhere in your code you you add somthis similar to this

    ...
    def StartGapPowerUp(self, value ):
        self.maxBrushSize = MaxBrushSize(value)
        time.sleep(10) #waits 10 seconds
        self.maxBrushSize = MaxBrushSize(default)
    ...

ive been playthingwith game development for many years in multiple languges so i have built some habits that i dont notic i do anymore lol.

---

next post will be some notes nd pointers of you latest code posted and ill put a script showing my previous answeres in action! stay tuned πŸ€“πŸ€“

Karina

1. self.point=Point(w() + self.position.w, h() - self.size.h)
If w() and self.size.w are the same here, then we got 2*self.position.w? And why instead of sec arg don't just right 0??

2. When I tried to use sleep(), my screen just got black and I couldn't close it. And also was a problem and it all hang and didn't move. I did smth and fixed it, don't know howπŸ˜…. But why theese things can happen?

I have hundred of questions on each couple lines)

stephen

@Karina

'''
I realized i didnt fully implemate Brushes and that might have been confusing.
so ill post a new script using actual brushes. 

and as a bonus for showing you actually want to learn and not just copy/paste 
ill add some extra functionality for your 'cookbook'. 


i do apologise about the amount on notations lol there beingbalot of comments
doesnt mean its a bd script. most is advice. i did change a few things mostly
for your convenience and to smooth out animation.

but first here is some notes on hat you have provided thus far..
'''
from scene import *
import random
import time


A = Action()

'''
i added these so you only have one place to change sizes instead of
bouncing around
    sw β‡’ screen width
    sh β‡’ screen height
    bw β‡’ block width
    bh β‡’ block height
    so β‡’ screen orientation
    lw β‡’ level view port width
    lh β‡’ level view port height
'''


def sw(): return get_screen_size()[0]
def sh(): return get_screen_size()[1]
def bw(): return 64
def bh(): return 96
def so(): return 1 if sw() < sh() else 2
def lw(): return 1024 if so() is 2 else 768
def lh(): return 1024 if so() is 1 else 768



def Ground(parent):
    return SpriteNode('plf:Ground_Grass', parent=parent, size=Size(bw(), bh()))



def ColumnBlock(parent):
    return SpriteNode('plc:Brown_Block', parent=parent, size=Size(bw(), bh()))

'''
not sure what happened here but as you 
probably know if called wil throw exceptions. 
im assuming this is why you dont actually use it.

  self.position = (self.size.w)

first a regular Node object hase no size property.
BUT it has a bbox wich is a scene.Rect with a width value. 
even with that said a poisition property much recieve a 
2 Tuple (x, y) preferably a Point object but not neccesary.
    i.e:
        self.position = Point(x, y)
let me know if your having a problem here it seem as your
trying to use self to refer to your Scene class. and you can.
but not through self. if your not sure how self work i can tell
 u jut say
'''
class BottomBrush(Node):
    def __init__(self):
        self.ground = GroundBlock(self)
        self.position = (self.size.w)


class Game(Scene):
    def setup(self):
        self.background_color = '#99d7ff'
        #to track when 5 seconds passed and need to add a new column
        self.time_passed = 0
        #add the first column so you don't have to wait for it 5 seconds
        self.columns = []
        self.add_column()
        x = 0
        ground = Node(parent=self)

        #building the upper and lower ground
        '''
            Generally a while loop is not a very big deal but in this
            case its not a great idea.
            i say this because what if for some unknown reason
            self.size.w is somthing crazy like 2389488754589433357. 
            πŸ€“πŸ˜… unlikely i know but just for fun...
            now your game is hung up seeming like it is frozen to end user.
            now a for loop, at least i feel, is much safer in this. matter.
            but to be honest.. a premade pillar object would be best use. ill 
            show this in next post. 
        '''
        while x < lw()+bw():
            lower_tile = SpriteNode('plf:Ground_Grass', 
                position=Point(x, 0), size=Size(bw(), bw()), anchor_point=(0.5, 0.0))
            higher_tile = SpriteNode('plf:Ground_GrassCenter', 
                position=Point(x, lh()), size=Size(bw(), bw()))
            x += bw()
            ground.add_child(lower_tile)
            ground.add_child(higher_tile)

        self.speed = 1 # Node.speed is defaulted to 1.0
        '''
        changed z_position to keep to and bottom over blocks
        '''
        ground.z_position = 999
    def update(self):
        self.time_passed += self.dt
        '''
        good job with >= hen comparing floats in this manner never use ==

        sence your calling self.column_moves() every frame we can manually
        move our blocks. (see method comment)
        # note: changing "5" to eithere a random int or an instance property
            will  alow a more dynamin level generation.
        '''
        if self.time_passed >= 5:
            self.add_column()
            self.time_passed = 0
        self.column_moves()


    def add_column(self):

        lower = random.randint(1, 360) // bw()
        higher = 9 - int(lower)
        #building the lower part
        y = 35
        '''
        here you can get rid of the variable "lower"
        this should reduse memory use. for this game it
        doesnt matter but in future it could make a diference. 
        use this instead..

              for i in range(random.randint(0, 360) // 64):

        this insures the memory is released after forloop.

        also i would suggest moving 
                block.anchor_point = (0.5, 0)
                            and
                block.position = (self.size.w, y)
        to your ColumnBlock function like this

            def ColumnBlock(parent, x, y):
                return SpriteNode('plc:Brown_Block',
                                    anchor_point=(0.5, 0),
                                    position=Point(x, y),
                                    parent=parent)

        then in your for loop make your "i" var useful and get rid of "y"

            for i in range(lower):
                self.columns.append(ColumnBlock(self, self.size.w, i*64))

        now we dont add a new block to memory then copy to list.  we
        just add one to list and memory at same time.

             anchor_point is values 0 to 1 
             0 being botom left a 1 being top/right
             (1, 1) would be top right
             (0.5, 0.5) would be center.
             changed block.size.h to 1.0
        '''
        for i in range(1, lower+1):
            block = ColumnBlock(parent=self)
            block.anchor_point = (0.5, 0.5)
            block.size= Size(bw(), bh())
            block.position = (lw(), bh()/3+i*bw())

            block.z_position = -i
            self.columns.append(block)
            y += bw()
        #building the higher part
        y = lh()
        for i in range(1, higher+1):
            block = ColumnBlock(parent=self)
            block.anchor_point = (0.5, 0.5)
            block.position = (lw(), (lh()-i*bh()/2))
            block.z_position = i
            self.columns.append(block)
            y -= bh()

    '''
    great work on this part only sugestion would be to set your interp timing.
    probably TIMING_SINODIAL  in this case cuz its mich smoother than linear.
    and this will go where you have "30/self.speed". and this oddly doesnt thow
    an excption and id avoid "0" for duration. 0.1 seems fast enough. or even 
    0.01 if needed. so somthing like ths.

            A.move_by(-self.size.w, 0.1, TIMING_SINODIAL)

    you also dont need remove. this is meant o remove objects not Actions.
    sinve you pass none or () its does nothing.

    one more thing the "self.speed is used to modify Action speed. but its 
    automatically implemented. to if you want a 2x animation speed just set
    self.speed = 2  and everything else is done for you."

    you also should useba Node object to group the sprites this way theres
    no need for the for loop. you just move the parent and child nodes will
    follow.
    i changed the folowing method so that it stops the "jerking"bwhen it 
    moves. they now move smoothly

    ::from update comment::
    with that all said, lets be nice to our cpu and instead of runing an Action 
    proccess lets just do some simple math. im not changing thisone but i would 
    write the following:
        #note: i wouldnt do this for every block i would group the colums (β—•_β—•) 
        #note: velocity would be set inside startup/__init__ and can be used
        #      with powerups ☺️
        for i in self.columns:
            i.position = Point(
                i.position[0] - self.velocity*self.dt, i.position[1])

    '''

    def column_moves(self):
        actions = [A.move_by(-self.size.w/2, 1, TIMING_SINODIAL)]
        for i in self.columns:
            i.run_action(A.sequence(actions))


'''
dont forget you can set some properties when calling the  "run()" function

scene.run()
## you know this one lol ##
    β‘΄ scene
## alloud orientations. 
-   FROM DOC: Important: The orientation 
-   parameter has no effect on iPads starting with iOS 10 because
-   it is technically not possible to lock the orientation in an app
-   that supports split-screen multitasking. ##
    β‘΅ orientation=DEFAULT_ORIENTATION 
## frame rate controle
    FROM DOC: By default, the scene’s update() method is called 60
    times per second. Set the frame_interval parameter to 2 for 
    30fps, 3 for 20, etc. ## 
    β‘Ά frame_interval=1
## this game wont need but here you go
    FROM DOC: Set this to True to enable 4x multisampling. This has
    a (sometimes significant) performance cost, and is disabled by
    default. ##
    β‘· anti_alias=False
## explains itself. i tent do make my own so i can control the 
    color and position. ##
    β‘Έ show_fps=False
## you shouldnt need this for this game if kept simple
    β‘Ή multi_touch=True
    )
'''
run(Game())

# now to write that version two example for u. and again great work!

stephen

@Karina said:

1. self.point=Point(w() + self.position.w, h() - self.size.h)
If w() and self.size.w are the same here, then we got 2*self.position.w? And why instead of sec arg don't just right 0??

2. When I tried to use sleep(), my screen just got black and I couldn't close it. And also was a problem and it all hang and didn't move. I did smth and fixed it, don't know howπŸ˜…. But why theese things can happen?

I have hundred of questions on each couple lines)

1.

self.point=Point(w() + self.position.w, h() - self.size.h) If w() and self.size.w are the same here, then we got 2self.position.w? And why instead of sec arg don't just right 0??*

self.position.w should be self.position.x
translated self.point=Point(w() + self.position.w, h() - self.size.h) means
Node's Position is a Point Object with x coordinate at screen width plus node's position x and y coordinate at screen height minus nodes hieght.

or in other words

x = screen_width + self.position.x
y = screen_hieght - self.size.hieght
self.position = Point(x, y)

2.

When I tried to use sleep(), my screen just got black and I couldn't close it. And also was a problem and it all hang and didn't move. I did smth and fixed it, don't know howπŸ˜…. But why theese things can happen?

this was my fault lol i forgot you need to att a decorator from ui module

   @ui.in_background
    def add_column(self):
        time.sleep(5)

this will run sleep on a different thread than your scene

stephen

@Karina

never too many questions

stephen

@Karina now im off to write the game in the way i would. so you can compare. in no way is MY way the best or even the most correct. but it is the way i learned to implement different objects and functionality over the years.

stephen

@Karina just an update ive been a bit busy but i might have my project ready in the next 20 hours or so. it wont have a lot of comments but ill put a few. but im trying to use only descriptive naming so it should not beva problem.hope to see what you learned from this project πŸ€“πŸ€“πŸ€“

Karina

@stephen said:

self.point=Point(w() + self.position.w, h() - self.size.h) <

Ah, I forgot that we're inside Node, not Scene

i understand what lw() does, but what is lower port width? And lw() is the returns the width, if add bw(), we should get beyond the screen?

self.size.w is somthing crazy like 2389488754589433357

I went to check the self.size.w, and it is 1024. What do you it is smth like thatπŸ‘†? And why in that case for is better than while?

stephen

@karina said:

@stephen said:

self.point=Point(w() + self.position.w, h() - self.size.h)
Ah, I forgot that we're inside Node, not Scene

i understand what lw() does, but what is lower port width? And lw() is the returns the width, if add bw(), we should get beyond the screen?

Response:

    sw β‡’ screen width
    bw β‡’ block width
    lw β‡’ level view port width

top part and lower part , at least in this case, are the same size width. so both top and bottom parts get their size width from our bw() Block Width.
Yes we do want to add AT LEAST block width past screen width.
Video Games are Feuled by immersion. You wand the End User to feel that our Level already exists.. so they must not see the objects pop into our level. so we do this just outside the view port. same for removinghe objects. we wait till they are at least Block Width negative position if block.w < -block.w: del block.
Also in our position checks we want to compare < for removing blocks and > for adding blocks and not <= and >=. by doing this we get a 1 point buffer insuring our create and delete actions are not visible bo End User.


@karina said:

@stephen said:

self.size.w is somthing crazy like 2389488754589433357

I went to check the self.size.w, and it is 1024. What do you it is smth like thatπŸ‘†? And why in that case for is better than while?

Response:

β€œ πŸ€“πŸ˜… unlikely i know but just for fun... ”

the value 2389488754589433357 this game is simple so this shouldnt happen. you coud see similar if you had bad execution order while using large open world type game environment...

since python has a GIL Global Interpreter Lock both the for and the whie loops are "blocking" and both can be used to achive hat you want.. The only prolem i can see making the for loop better choice here is that a while loop has the possibility of being infinit if somthing doesnt go right where the for loop has a predefined end point. insuring the game will not freeze forever do to your code withen.

---

did i cover what you were confused for these? im not sure i completey understook your questions. πŸ™ƒ

Karina

I thought of grouping the blocks but don't know how to do it.

It's quite difficult to me to formulate them now cause I don't have anything to write on and can't buy it😿 But:

I don't know what the timing_sinodial, timing_ease in and others mean. I tried to play with them in examples but didn't see the difference, also anti-alias and its 4x multisampling
And the Brushes, so didn't implement it and MaxSizeBrush, but maybe I will when do the jumps

stephen

@Karina ill whip up a quick example that will show the diferences and ill Annotate as much as i can πŸ™ƒ hang tight

what do you mean you have nothing to write on?

Karina

@stephen a notebook or smth and I wouldn't have to look what was in the beginning and had important things in front of me

stephen

@Karina said:

@stephen a notebook or smth and I wouldn't have to look what was in the beginning and had important things in front of me

what are you using currently? and im sorry im taking so long to get back but i read overball the comments and im not sure i covered all the questions before. do you still not understand how the following works?

  • w()
    -update(self)
    -self.dt
    -self
    -self.size.w
    -lw()

my final example full game will include all these and more but i dont want you to throw a full script at you unless you can comprehend a good amount. most of what has been covered has been mostly linear and the full script ill feel like a spider web lol but ill annotate as much as i can.

Karina

w()
-update(self)
-self.dt
-self
-self.size.w
-lw()

This yes, here it looks quite easy.
I'm trying to write for now like I can, though it's not the best way and I get annoying bugs sometimes
What it means that it all was linear?

the full script ill feel like a spider web lol but ill annotate as much as i can.

Wow I'm scared

Karina

@stephen You said @in_background makes smth run on a dif thread. I'm not into the ui for now, but what is the dif thread?

stephen

@Karina what i mean by linear is the scripts are writen in a step by step manner. and then the rest is all focused inside the loop body. just a way i refer to simple writing techniques. where alternatively i tend write in a manner that jumps around. multiple inheritence, conroller types, delegates.. and so on.

@karina said:

@stephen You said @in_background makes smth run on a dif thread. I'm not into the ui for now, but what is the dif thread?

ui.in_background() decorator executes the following process in the main thread instead of on your current event loop's. whenever you call module level run() function from scene or class method View.Present()from ui you create a new event loop separate from Interpreters main thread. Python uses GIL (Global Interpreter Lock) that only alows one thread. you can still use multiprocessing though. for info on that check out Asynchronous programming. the reason for GIL is objC memory managment is not considered "Thread Safe".

Docs said:

ui.in_background(fn):

Call a function on the main interpreter thread. This is useful to perform long-running tasks from an action or delegate callback without blocking the UI. It can also be used for functions that don’t support being called from the main UI thread (e.g. console.alert()).

as far as you not using the ui module... you have been this whole time.. lol scene uses ui quite heavely. and you dont need to import ui separatly. by using from scene import * ui is also included. from your game just call ui. and then whatever you want from there. when you run your scene game it actually prepares the ui stuff for you and then presents a SceneView if you setup a custom View class in your game script you can create a scene.SceneView and set its SceneView.scene property to your Game Object. then instead of using run(MyScene()) you present the View which places your game in its own frame on your screen making it easy to add ui objects as your gui for ur game.

stephen

@Karina and im almost finished with the Animation/Actions demo. should be posted later this evening.

currently 5pm my time

Karina

@Karina and im almost finished with the Animation/Actions demo. should be posted later this evening.
currently 5pm my time

πŸ‘

Karina
def touch_began(self, touch):
        sound.play_effect('arcade:Jump_4')
        self.player.texture = player_textures[3]
        jumping = [A.move_by(0, 100, 0.2), A.wait(1), A.move_by(0, -100, 0.1)]
        self.player.run_action(A.sequence(jumping)

I tried to make him jump but it doesn't work. I know there should be a more clever way to do that but I have no idea

Drizzel

@Karina I haven’t used Actions much, I prefer animating nodes myself. But theoretically, from reading the docs, your code should work (assuming your indentation is correct and your actual code doesn’t miss the closed bracket at the end)

Karina

@stephen you mean fixing the Node pos by yourself? Action should work smoother and easier to write. If it also worked here)
And why in your program you called this blocks brushes?πŸ–Œ

Karina

I began to read about threads and sometimes don't understand what about this all, but in general understand. And for what here to use them? It encreases the speed of smth?

stephen

@Karina @Drizzel here is a demo for action timing. i intended to have much more informative and visually attractive version and ill improve it in time but for noe you can see a visual difference between each interpolation.

note: this is formated for ipad only. future versions will have formatting for other devices. i appoligise.

from scene import *

class BaseSign(SpriteNode):
    def __init__(self, texture=None, tag='Sign', *args, **kwargs):
        super().__init__(texture=texture, *args, **kwargs)
        self.anchor_point=(0.0, 1.0)
        self.position = Point(10, get_screen_size()[1]-10)
        self.z_position=1
        self.color='#000000'
        self.size=Size(get_screen_size()[0]-20, get_screen_size()[1]-20)
        self.bg=SpriteNode(
            texture=None, 
            color='#80cdff', 
            position=Point(3, -3),
            anchor_point=(0.0, 1.0),
            size=Size(get_screen_size()[0]-26, get_screen_size()[1]-26),
            parent=self)
        self.sign=LabelNode(
            '',
            position=Point(1, 15),
            z_position=3,
            font=('Ubuntu Mono', 18), 
            parent=self.bg, 
            color='#000000',
            anchor_point=(0.0, 1.0))    


class MyScene (Scene):
    def setup(self):
        self.dur=5
        self.moveToPos=(73, 245)

        self.sign=BaseSign(parent=self, position=self.size/2)

        self.sign.sign.text=self.Text()
        for x in range(16):
            b=SpriteNode(Texture('pzl:BallGray'),
            size=Size(16, 16),
            color='#00f90b',
            position=Point(73, (get_screen_size()[1]-132) - 36.15*x),
            z_position=9,
            parent=self)
            b.run_action(
                Action.repeat(
                    Action.sequence(
                        Action.call(self.UpdateTime, 0.1),
                        Action.wait(0.5),
                        Action.move_to(self.moveToPos[1], b.position[1], 2, x),
                        Action.move_to(self.moveToPos[0], b.position[1], 2, x)),-1))



    def UpdateTime(self, node, progress):
        if self.speed is 1:
            self.speed=0.5
        else:
            self.speed=1
        self.sign.sign.text=self.Text()

    def Text(self):
        return f'''
    ╔════════════════════════╗
    β•‘    BUILT_IN_CONSTANT   β•‘
    ╠════╦════════╦══════════╣
    β•‘ 2s β•‘ 2 part β•‘ 1x speed β•‘
    ╠════╩════════╩══════════╣
    β•‘TIMING_LINEAR           β•‘
    β•Ÿβ•β•‘                    β•žβ•β•’
    β•‘TIMING_EASE_IN_2        β•‘
    β•Ÿβ•β•‘                    β•žβ•β•’
    β•‘TIMING_EASE_IN_2        β•‘
    β•Ÿβ•β•‘                    β•žβ•β•’
    β•‘TIMING_EASE_OUT         β•‘
    β•Ÿβ•β•‘                    β•žβ•β•’
    β•‘TIMING_EASE_OUT_2       β•‘
    β•Ÿβ•β•‘                    β•žβ•β•’
    β•‘TIMING_EASE_IN_OUT      β•‘
    β•Ÿβ•β•‘                    β•žβ•β•’
    β•‘TIMING_SINODIAL         β•‘
    β•Ÿβ•β•‘                    β•žβ•β•’
    β•‘TIMING_ELASTIC_OUT      β•‘
    β•Ÿβ•β•‘                    β•žβ•β•’
    β•‘TIMING_ELASTIC_IN       β•‘
    β•Ÿβ•β•‘                    β•žβ•β•’
    β•‘TIMING_ELASTIC_IN_OUT   β•‘
    β•Ÿβ•β•‘                    β•žβ•β•’
    β•‘TIMING_BOUNCE_OUT       β•‘
    β•Ÿβ•β•‘                    β•žβ•β•’
    β•‘TIMING_BOUNCE_IN        β•‘
    β•Ÿβ•β•‘                    β•žβ•β•’
    β•‘TIMING_BOUNCE_IN_OUT    β•‘
    β•Ÿβ•β•‘                    β•žβ•β•’
    β•‘TIMING_EASE_BACK_IN     β•‘
    β•Ÿβ•β•‘                    β•žβ•β•’
    β•‘TIMING_EASE_BACK_OUT    β•‘
    β•Ÿβ•β•‘                    β•žβ•β•’
    β•‘TIMING_EASE_BACK_IN_OUT β•‘
    β•Ÿβ•β•‘                    β•žβ•β•’
    β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•'''



if __name__ == '__main__':

    run(MyScene(), show_fps=True)


Drizzel

@Karina I think you got me mixed up with @stephen :) And yes, I do prefer to fix the nodeβ€˜s position manually.

My version of flappy bird I posted here some days ago is a good example. I used basic physics formulas to give the bird an acceleration, and then use that to calculate the birdβ€˜s speed and thus position at a given time. Then adjusting the gravity and weight parameters made the motion feel β€œjust right” (in my opinion).

Technical stuff aside, I just do it for greater control, so I can get the feeling of motions right.

Drizzel

@stephen looking good! I always was a little too lazy to try them all out... and I’m not using scene much nowadays.

stephen

@Karina said:

@stephen you mean fixing the Node pos by yourself? Action should work smoother and easier to write. If it also worked here)
And why in your program you called this blocks brushes?πŸ–Œ

i did mean changing the nodes pos manually and its prety smooth esspecially when you implement your own interpolation (the TIMING constants used with Actions).

what my main concern was that you were creating an Action Proccess for every block instead of having one Action Process for a group of blocks. this group would be the brushes i keep talking about.

in my older exampe i did refer to blocks as brushes and then later i corrected myself. in my final example (the game not the action demo and i havnt finished my version of the game yet) will have "brushes" and everything will be anotated so you its easy to understand each part.

Brush: predefined graphical structure to be instantiated multiple times. often cached for fast referencing." Example: Trees and Vilages in MineCraft..

Drizzel

@Karina said:

I began to read about threads and sometimes don't understand what about this all, but in general understand. And for what here to use them? It encreases the speed of smth?

I certainly don’t know everything about threads either, but here’s my understanding of them.

The basics:
* I think of threads like... well threads, as in strings or a thin rope. And when you open a thread, the code in this thread is run separated to other threads.

  • Thus Threads allow for separate codes to run β€œat the same time” (this technically isn’t possible, and is done through some tricks so that it appears so. Thus the quotation mark. But I’m not here to confuse you, so let’s go on)

  • Therefore you can have one section of code that (for example) has a time.sleep(100000) in it, and a thread that prints stuff in an infinite loop. Since they run in different threads, your code will still print stuff, even though there’s a stupidly long sleep statement in it

Edit If you want some coding examples, I can write you some with comments

stephen

@Drizzel @Karina

Quick Update..

Nearly finished with the example game. I usually dont take this long but lifes been crazy lol

Done:

  • EventManager
  • block type classes
  • GameObject class for creating individual objects
  • Brush class for creating brush objects (template groups of GameObjects)
  • land and sky generation
  • Obsticle Gap Generation (better flow of gap hieght generation)
  • Animation Class (added to GameObjects and Brushes as Instance Properties)
  • Standard Obsticle Brush (objects with gap to flap/fly through)
  • Connection of GameObjects, Brush Objects, Animations, EventManager and Scene Class. (pretymuch everyone can talk to everyone and all get Tick/Update calls to stay current)

TODO:

  • Player class
  • points score and rewards system
  • Main Menu
  • Settings Menu (maybe.. depends on time)
  • PowerUps/Buffs/Boosters
  • Game State Handle

NOTE:

@Karina did the Action timing demo help you understand the differences between each? it wasnt as informative as i like but figured it was ok givin the visual aspect

Karina

@Drizzel But in pythonista threads can't run in parallel, so it shouldn't increase the speed. So what benefits we have?
And when I put sleep, I don't want things to print

Quick Update..

Already some familiar things in it)

Karina

@Karina did the Action timing demo help you understand the differences between each? it wasnt as informative as i like but figured it was ok givin the visual aspect

@stephen Yes but it seems that all they are alike, some are nearly the same. But what the columns 2s, 2 part, 1x speed?
And how did you built this table?)

And I'm learning, most of time with my ipad, the most important that it's suitable for it

Drizzel

@Karina Now yes, threads may not always speed up your program. But they can keep your program responsive even though there’s some time consuming task being done. I also sometimes use them to constantly check if a variable changed, and then trigger a function. (There’s better ways to do that. But hey, I’m learning too.)

Even though this is in no way specific to Pythonista, let’s take a slight detour. Some time ago, I built a door manager for the chicken pen of my family (yeah, it’s a bit unusual). It opens and closes the door at given times, but also constantly checks a button and email for a manual overrides. Since the button needs to be checked every 0.1 seconds, but checking the email takes about 4 seconds (establishing a connection to the google servers takes some time), the button wouldn’t be checked while the code is trying to get the new email. This is solved with threads, so they get checked in parallel, not after each
Therefore the button stays responsive, even though checking mails uses up a lot of time

Karina

@Drizzel Sorry, i've not so understood what have you done. What means door manager for chicken pen? Your family has a chicken that has a pen and needs door manager??) what
But what here does the thread I got

Drizzel

@Karina Yeah, we have a little β€œchicken house” and I wrote a script that opens and closes the door by turning on and off a motor. But that’s not the point :) I just needed an example to better illustrate the uses of threads

stephen

@Drizzel @Karina

First A Litle On The Thread Conversation

this comes from years experience ith event driving C# programing, but doesnt mean its correct πŸ˜‰

Stop thinking so much about threads ☺️. multiprocessing is what we do.


StackOverflow said:

What is meant by synchronous and asynchronous?

Synchronous basically means that you can only execute one thing at a time. Asynchronous means that you can execute multiple things at a time and you don't have to finish executing the current thing in order to move on to next one.


https://www.techwalla.com β€Ί articles said:

Which is faster synchronous or asynchronous?

Asynchronous transfers are generally faster than synchronous transfers. This is because they do not take up time prior to the transfer to coordinate their efforts. However, because of this, more errors tend to occur in asynchronous transfers as opposed to synchronous transfers.


I know its not a great source but its not always the worst πŸ˜‰πŸ™ƒ
https://en.m.wikipedia.org/wiki/Asynchronous_I/O said:

In computer science, asynchronous I/O (also non-sequential I/O) is a form of input/output processing that permits ...

---

@Karina said:

@stephen Yes but it seems that all they are alike, some are nearly the same. But what the columns 2s, 2 part, 1x speed?
And how did you built this table?)

They are very similar yes but when you combine them in different ways with multiple objects you can see the way they are differnent.

this is already shown in my game example that is nearly finished. i think you will benifit alot from it. im trying to write it as clearly as i can which is why its taking so long..

the 2s 2 part 1x speed is just data not colums but i can see how i made it sem that way but its just info about the example.

2s β†  2 seconds animation duration per part.

2 part β†  loop is 2 parts part one is ●⇒ and part two is⇐●

1x speed β†  the Scene's Scene.speed is set to 1

i put them in just to clearify settings. ☺️


the lines/borders i used are Unicode from the Drawing chart. i have already included these in the Example script i modified here for Special Characters.py the comments instruct how to se it πŸ™ƒπŸ€“

well back to the game lol

Karina

@stephen it's ok that it takes time, I'm trying to read your and Drizzel's programs and adding things to mine

Karina

@Drizzel can you show a short example how you do animation? I have experience only with Action

stephen

@Karina

heres an example of a animation layer. i use group of sequences then stack oher groups in objects to create my deired effect. and the gam ont be production worthy just simple with my touch 😎 but im using many different techniques for see. nearly done just couple more classes nd thn annotations and its all ya'lls


def Vibrate(self, repeat, speed, t=TIMING_LINEAR):
        self.node.run_action(
            a.repeat(
                a.group(
                    a.sequence(
                        a.move_by( 5, 5, speed, t),
                        a.move_by(-5,-5, speed, t),
                        a.move_by(-5, 5, speed, t),
                        a.move_by( 5,-5, speed, t)),
                    a.sequence(
                        a.scale_y_by( 0.1, speed, t),
                        a.scale_y_by(-0.1, speed, t),
                        a.scale_y_by( 0.1, speed, t),
                        a.scale_y_by(-0.1, speed, t))
                    ),repeat))

Karina
    def player_flies(self):
        g = gravity()
        speed = 1000
        if abs(g.x) > 0.05:
            y = self.player.position.y
            self.player.position.y = min(sh(), max(41, y + speed*g.y))
            print(self.player.position.y)

I tried to move him with gravity but also doesn't work, like player.position.y is unchangeable or = doesn't work
Don't know what to doπŸ˜’

stephen

@Karina said:

def player_flies(self): g = gravity() speed = 1000 if abs(g.x) > 0.05: y = self.player.position.y self.player.position.y = min(sh(), max(41, y + speed*g.y)) print(self.player.position.y)

I tried to move him with gravity but also doesn't work, like player.position.y is unchangeable or = doesn't work
Don't know what to doπŸ˜’

try changing min(sh(), max(41, y + speed*g.y)) to
min(sh(), max(41, self.player.position.y + speed*g.y))
that shouldnt change anything but just a debugging step..

then try using:


def player_flies(self, speed=100,):
    if gravity()[1] > 0.5:
        x, y = self.player.position
        self.player.position = Point(x, (y + speed) * gravity()[1])
        print(self.player.position[1])


from my knowledge you cannot set position.y you have to set the whole point.
also if you want the player to move upwards by tilting the top of device down. you must multiply by -1 gravity()[1] * -1 that way your output is positive. also the x, y, z properties of gravity are real numbers 0.0-1.0 just incase you didnt know. πŸ™ƒ

Also my example game is just about ready. just doing some gui stuff and Annotations.

Karina

@stephen actually I want it to jump when I touch, but I couldn't do this. Now I get what was the problem but didn't fix it

I always forget to use the Point for position, can you show why it's better?

Karina

@stephen i've read your code with brushes, got it but have some little questions

1

class TopBrush(Node):
    def __init__(self, brushSize, *args, **kwargs):
        self.base=BaseBlock(self)
        self.size=Size(64, 64)
        self.position=Point(w()+(self.size.w), h()-self.size.h)
        self.brushSize=brushSize
        self.blocks=list([self.base, ])
        #what means the , in the end of the list? And how we add base blocks of next brushes to the list?

2, 3

class MyScene (Scene):
    def setup(self):
        self.background=SpriteNode(Texture('plf:BG_Colored_grass'), 
            size=Size(w(), h()), position=self.size/2, parent=self)
        self.preset=[(1, 7),(2, 6),(3, 5),(4, 4),(5, 3),(6, 2),(7, 1)]
        self.i=0.0
        self.brushSpawnTimer=3.0
        self.brushes=list([])
        #But the [] is already list
        self.scrollSpeed=40

        for x in range(int((w()*128)/64)):
            #so it's w()*2? Why 2?
            self.brushes.append(Stone(self, x))
Karina

@stephen said:

Vibrate(self, repeat, speed, t=TIMING_LINEAR):
self.node.run_action(
a.repeat(
a.group(
a.sequence(
a.move_by( 5, 5, speed, t),
a.move_by(-5,-5, speed, t),
a.move_by(-5, 5, speed, t),
a.move_by( 5,-5, speed, t)),
a.sequence(
a.scale_y_by( 0.1, speed, t),
a.scale_y_by(-0.1, speed, t),
a.scale_y_by( 0.1, speed, t),
a.scale_y_by(-0.1, speed, t))
),repeat))

I think I have a problem in my action that after moving up there's down, so it does nothing. But here you do it😳

stephen

@Karina said:

@stephen i've read your code with brushes, got it but have some little questions

1
```
class TopBrush(Node):
def init(self, brushSize, args, *kwargs):
self.base=BaseBlock(self)
self.size=Size(64, 64)
self.position=Point(w()+(self.size.w), h()-self.size.h)
self.brushSize=brushSize
self.blocks=list([self.base, ])
#what means the , in the end of the list? And how we add base blocks of next brushes to the list?

```

2, 3
```
class MyScene (Scene):
def setup(self):
self.background=SpriteNode(Texture('plf:BG_Colored_grass'),
size=Size(w(), h()), position=self.size/2, parent=self)
self.preset=[(1, 7),(2, 6),(3, 5),(4, 4),(5, 3),(6, 2),(7, 1)]
self.i=0.0
self.brushSpawnTimer=3.0
self.brushes=list([])
#But the [] is already list
self.scrollSpeed=40

    for x in range(int((w()*128)/64)):
      #so it's w()*2? Why 2?
        self.brushes.append(Stone(self, x))

```

that comma is optional but it just indicates that you intend on adding more to the list. just a habbit.

that brush example is not good. my game example will show u MANY things throught and is currently at 1400 lines without comments. i should have it posted in the next 8 hours. this script im making will show u actual brushes.. i would ignore the old one.

and to add bade block to that list just do self.blocks.append(block)

stephen

@Karina said:

@stephen actually I want it to jump when I touch, but I couldn't do this. Now I get what was the problem but didn't fix it

I always forget to use the Point for position, can you show why it's better?

jump on tap is prety easy. in your touch_began trigger somthing like the following for a very simple way.


def touch_began(self, touch):
    self.player.run_action(
        a.sequence(
            a.move_by(128, 64, 0.5, TIMING_LINEAR),
            a.wait(0.1),
            a.move_by(128, 128, 2, TIMING_LINEAR)), 'tag:player-jump')


this should give an abrupt leap then more of a glide back down. the wait is just a buffer for junt in case and use whatever timing that looks best for ur needs. this is not desinged for uneven ground. u need collision checks for uneven and i show that in my new exmple

stephen

@Karina said:

def player_flies(self): g = gravity() speed = 1000 if abs(g.x) > 0.05: y = self.player.position.y self.player.position.y = min(sh(), max(41, y + speed*g.y)) print(self.player.position.y)

I tried to move him with gravity but also doesn't work, like player.position.y is unchangeable or = doesn't work
Don't know what to doπŸ˜’

in my earlierpost i show that you must set the hole position you cannot set just x or y

stephen

@Karina said:

@stephen said:

Vibrate(self, repeat, speed, t=TIMING_LINEAR):
self.node.run_action(
a.repeat(
a.group(
a.sequence(
a.move_by( 5, 5, speed, t),
a.move_by(-5,-5, speed, t),
a.move_by(-5, 5, speed, t),
a.move_by( 5,-5, speed, t)),
a.sequence(
a.scale_y_by( 0.1, speed, t),
a.scale_y_by(-0.1, speed, t),
a.scale_y_by( 0.1, speed, t),
a.scale_y_by(-0.1, speed, t))
),repeat))

I think I have a problem in my action that after moving up there's down, so it does nothing. But here you do it😳

this was an example of doing a vibration effect. so it needs to move even amounts in directions because its doesnt move anywhere. just "shakes"

was mainly meant to show you a repeated group with sequence sections for action structures

Karina
def touch_began(self, touch):
    self.player.run_action(
        a.sequence(
            a.move_by(128, 64, 0.5, TIMING_LINEAR),
            a.wait(0.1),
            a.move_by(128, 128, 2, TIMING_LINEAR)), 'tag:player-jump')

But it means he moves on 128 forward, cause move_by(dx, dy, duration, timing_mode). And after wait he should move Dow, so dy with minus? And what is 'tag:player-jump'?

Karina

my game example will show u MANY things throught and is currently at 1400 lines

@stephen this is *10 more than anything I have ever read😳 Don't worry, you can't show too little things in itπŸ˜…
I should be an easy game, I thought I the beginning

stephen

@Karina ill be posting the example shortly

stephen

@Karina said:

def touch_began(self, touch): self.player.run_action( a.sequence( a.move_by(128, 64, 0.5, TIMING_LINEAR), a.wait(0.1), a.move_by(128, 128, 2, TIMING_LINEAR)), 'tag:player-jump')

But it means he moves on 128 forward, cause move_by(dx, dy, duration, timing_mode). And after wait he should move Dow, so dy with minus? And what is 'tag:player-jump'?

yes your correct i made a boo boo lol i didnt test i just wrote

def touch_began(self, touch):
    self.player.run_action(
        a.sequence(
            a.move_by(128, 64, 0.5, TIMING_LINEAR),
            a.wait(0.1),
            a.move_by(-128, -128, 2, TIMING_LINEAR)), 'tag:player-jump')

Karina

@stephen still nothing. I just did copy paste for not to do mistakes. Maybe it's somewhere else in the code

from scene import *
import sound
import random
import time


def sw(): return get_screen_size()[0]
def sh(): return get_screen_size()[1]
def bw(): return 64
def bh(): return 96
def so(): return 1 if sw() > sh() else 2
def lw(): return 1024.0 if so() == 1 else 768.0
def lh(): return 1024.0 if so() == 2 else 768.0 


A = Action()
player_textures = [Texture('plf:AlienBeige_walk1'),
Texture('plf:AlienBeige_walk2'), Texture('plf:AlienBeige_hit'), Texture('plf:AlienBeige_jump'), Texture('plf:AlienBeige_stand')]


def GroundBlock(parent):
    return SpriteNode('plf:Ground_Grass', parent=parent)


def ColumnBlock(parent, x, y):
    return SpriteNode('plc:Brown_Block', parent=parent, anchor_point = (0.5, 0), position = Point(x, y))


class BottomBrush(Node):
    def __init__(self):
        self.ground = GroundBlock(self)
        self.position = (self.size.w)


class Game(Scene):
    def setup(self):
        self.background_color = '#99d7ff'
        #to track when 5 seconds passed and need to add a new column
        self.time_passed = 0
        #add the first column so you don't have to wait for it 5 seconds
        self.columns = []
        self.moveTo = (220, 120)
        self.add_column()
        x = 0
        ground = Node(parent=self)
        ground.z_position = -1
        #building the upper and lower ground
        while x < self.size.w:
            lower_tile = SpriteNode('plf:Ground_Grass', position = Point(x, 30))
            higher_tile = SpriteNode('plf:Ground_GrassCenter', position = Point(x, 738))
            x += bw() - 2
            ground.add_child(lower_tile)
            ground.add_child(higher_tile)

        self.player = SpriteNode('plf:AlienBeige_front', parent=self)
        self.player.position = (40, 120)
        self.play_effect = 0
        self.game_over = False


    def update(self):
        if self.t > 1:
            self.player_runs()
        self.time_passed += self.dt
        if self.time_passed >= 1.5:
            self.add_column()
            self.time_passed = 0
        self.column_moves()
        self.player_hit()
        if self.game_over == True and self.player.position.y < 30:
            self.player.remove_from_parent()

    def touch_began(self, touch):
        #self.player_jumps()
        self.player.run_action(
        A.sequence(
            A.move_by(128, 64, 0.5, TIMING_LINEAR),
            A.wait(0.1),
            A.move_by(-128, -128, 2, TIMING_LINEAR)), 'tag:player-jump')


    def new_game(self):
        A.wait(2)
        for i in self.columns:
            i.remove_from_parent()
        self.player.texture = player_textures[4]
        self.player.position = (40, 120)


    def add_column(self):
        lower = random.randint(0, 360) // bw()
        #building the lower part
        for i in range(lower):
            self.columns.append(ColumnBlock(self, self.size.w + bw(), i*64 + 55))
        #building the higher part
        for i in range(9 - lower):
            self.columns.append(ColumnBlock(self, self.size.w + bw(), (self.size.h - 100) - i*64))


    def column_moves(self):
        action = A.move_by(-self.size.w - bw(), 0, 0.7, TIMING_SINODIAL)
        for i in self.columns:
            i.run_action(action)


    def player_runs(self):
        self.player.x_scale = 1
        walking_sounds = ['rpg:Footstep04', 'rpg:Footstep05']
        if self.player.position.x <= 200:
            step = int(self.player.position.x/5) % 2
            self.player.texture = player_textures[step]
            action = A.move_by(20, 0, 1.5)
            self.player.run_action(action)
            self.play_effect += 1
            if self.play_effect % 30 == 0:
                sound.play_effect(walking_sounds[step])


    def player_hit(self):
        player_hitbox = Rect(self.player.position.x - 10, self.player.position.y + 10, 10, 10)
        for c in self.columns:
            if c.frame.intersects(player_hitbox):
                self.player.texture = player_textures[2]
                self.player.run_action(A.move_by(0, -200, 0.2))
                sound.play_effect('arcade:Hit_2')
                self.game_over = True


    def player_jumps(self):
         self.player.run_action(
        A.sequence(
            A.move_by(128, 64, 0.5, TIMING_LINEAR),
            A.wait(0.1),
            A.move_by(-128, -128, 2, TIMING_LINEAR)), 'tag:player-jump')



run(Game())
stephen

@Karina i added isGrounded and adjusted the Actions. it needs some fine tuning but he jumps. im finalizing my game now so after a few touchups ill postit here


from scene import *
import sound
import random
import time


def sw(): return get_screen_size()[0]
def sh(): return get_screen_size()[1]
def bw(): return 64
def bh(): return 96
def so(): return 1 if sw() > sh() else 2
def lw(): return 1024.0 if so() == 1 else 768.0
def lh(): return 1024.0 if so() == 2 else 768.0


A = Action()
player_textures = [Texture('plf:AlienBeige_walk1'),
Texture('plf:AlienBeige_walk2'), Texture('plf:AlienBeige_hit'), Texture('plf:AlienBeige_jump'), Texture('plf:AlienBeige_stand')]


def GroundBlock(parent):
    return SpriteNode('plf:Ground_Grass', parent=parent)


def ColumnBlock(parent, x, y):
    return SpriteNode('plc:Brown_Block', parent=parent, anchor_point = (0.5, 0), position = Point(x, y))


class BottomBrush(Node):
    def __init__(self):
        self.ground = GroundBlock(self)
        self.position = (self.size.w)


class Game(Scene):
    def setup(self):
        self.background_color = '#99d7ff'
        #to track when 5 seconds passed and need to add a new column
        self.time_passed = 0
        #add the first column so you don't have to wait for it 5 seconds
        self.columns = []
        self.moveTo = (220, 120)
        self.add_column()
        x = 0
        ground = Node(parent=self)
        ground.z_position = -1
        #building the upper and lower ground
        while x < self.size.w:
            lower_tile = SpriteNode('plf:Ground_Grass', position = Point(x, 30))
            higher_tile = SpriteNode('plf:Ground_GrassCenter', position = Point(x, 738))
            x += bw() - 2
            ground.add_child(lower_tile)
            ground.add_child(higher_tile)

        self.player = SpriteNode('plf:AlienBeige_front', parent=self)
        self.player.position = (40, 120)
        self.play_effect = 0
        self.game_over = False
        self.isGrounded=True



    def update(self):

        if self.t > 1:
            self.player_runs()
        self.time_passed += self.dt
        if self.time_passed >= 1.5:
            self.add_column()
            self.time_passed = 0
        self.column_moves()
        self.player_hit()
        if self.game_over == True and self.player.position.y < 30:
            self.player.remove_from_parent()

    def touch_began(self, touch):
        self.player_jumps()



    def toggle_Grounded(self):
        self.isGrounded = not self.isGrounded


    def new_game(self):
        A.wait(2)
        for i in self.columns:
            i.remove_from_parent()
        self.player.texture = player_textures[4]
        self.player.position = (40, 120)


    def add_column(self):
        lower = random.randint(0, 360) // bw()
        #building the lower part
        for i in range(lower):
            self.columns.append(ColumnBlock(self, self.size.w + bw(), i*64 + 55))
        #building the higher part
        for i in range(9 - lower):
            self.columns.append(ColumnBlock(self, self.size.w + bw(), (self.size.h - 100) - i*64))


    def column_moves(self):
        action = A.move_by(-self.size.w - bw(), 0, 0.9, TIMING_SINODIAL)
        for i in self.columns:
            i.run_action(action)


    def player_runs(self):
        self.player.x_scale = 1
        walking_sounds = ['rpg:Footstep04', 'rpg:Footstep05']
        if self.player.position.x <= 200 and self.isGrounded:
            step = int(self.player.position.x/5) % 2
            self.player.texture = player_textures[step]
            action = A.move_by(20, 0, 0.5)
            #self.player.run_action(action)
            self.play_effect += 1
            if self.play_effect % 30 == 0:
                sound.play_effect(walking_sounds[step])


    def player_hit(self):
        player_hitbox = Rect(self.player.position.x - 10, self.player.position.y + 10, 10, 10)
        for c in self.columns:
            if c.frame.intersects(player_hitbox):
                self.player.texture = player_textures[2]
                self.player.run_action(A.move_by(0, -200, 0.2))
                sound.play_effect('arcade:Hit_2')
                self.game_over = True


    def player_jumps(self):
        self.player.run_action(
            A.sequence(
                A.move_by(32, 128, 0.5, TIMING_LINEAR),
                A.move_to(self.player.position[0]+128, 120, 0.5, TIMING_LINEAR)))

run(Game())


stephen

here is the game lol made new thread fo it

Karina

@stephen thank you, I've began to read it

stephen

@Karina Awesome if you have any questions please post on the new thread πŸ˜‰

stephen

@Karina also "brushes" are properly used in the example so hopfully you see the diference