Forum Archive

Help me find a way to make the lasers fire in a certain direction.

BurntRice

I need the type of code so that depending on which direction button was pressed last, the lasers fires in that direction. My code so far only shoots in a positive x axis. I need it to shoot in negative x axis, and positive and negative y axis as well.
Here is my code so far:

from scene import *
import turtle 
import ui 
import math
import random
A = Action()
#Setting up background colour for the entire scene
class Game(Scene):

    def setup(self):
        self.bg = SpriteNode('spc:BackgroundBlack')
        self.lasers = []

        #Creating the player
        self.Player = SpriteNode('shp:RoundRect')
        self.Player.color = ("cyan")
        self.Player.anchor_point = (0.5, 0)
        self.Player.position = (512, 400)
        self.add_child(self.Player)

        #Controlling the player
        self.UpCrtl = SpriteNode('iow:arrow_up_b_32')
        self.UpCrtl.x_scale = 3.5/1.0
        self.UpCrtl.y_scale = 3.5/1.0
        self.UpCrtl.alpha = 0.5
        self.UpCrtl.position = (175, 295)
        self.add_child(self.UpCrtl)

        self.DownCrtl = SpriteNode('iow:arrow_down_b_32')
        self.DownCrtl.x_scale = 3.5/1.0
        self.DownCrtl.y_scale = 3.5/1.0
        self.DownCrtl.alpha = 0.5
        self.DownCrtl.position = (175, 130)
        self.add_child(self.DownCrtl)

        self.RightCrtl = SpriteNode('iow:arrow_right_b_32')
        self.RightCrtl.x_scale = 3.5/1.0
        self.RightCrtl.y_scale = 3.5/1.0
        self.RightCrtl.alpha = 0.5
        self.RightCrtl.position = (250, 212.5)
        self.add_child(self.RightCrtl)

        self.LeftCrtl = SpriteNode('iow:arrow_left_b_32')
        self.LeftCrtl.x_scale = 3.5/1.0
        self.LeftCrtl.y_scale = 3.5/1.0
        self.LeftCrtl.alpha = 0.5
        self.LeftCrtl.position = (100, 212.5)
        self.add_child(self.LeftCrtl)

        #The button for shooting
        self.laserButton = SpriteNode('shp:Circle')
        self.laserButton.color = ('gray')
        self.laserButton.x_scale = 3/1
        self.laserButton.y_scale = 3/1
        self.add_child(self.laserButton)
        self.laserButton.position = (1000, 212.5)

    def update(self):
        for touch in self.touches.values():
            if touch.location in self.LeftCrtl.bbox:
                #left button pressed
                new_x = self.Player.position.x - 5
                if new_x >= 0 and new_x <= 1100:
                    self.Player.position = (new_x, self.Player.position.y)

            if touch.location in self.RightCrtl.bbox:
                    #right button pressed
                new_x = self.Player.position.x + 5
                if new_x >= 0 and new_x <= 1100:
                    self.Player.position = (new_x, self.Player.position.y)

            if touch.location in self.UpCrtl.bbox:
                new_y = self.Player.position.y + 5
                if new_y >= 0 and new_y <= 800:
                    self.Player.position = (self.Player.position.x, new_y)

            if touch.location in self.DownCrtl.bbox:
                new_y = self.Player.position.y - 5
                if new_y >= 0 and new_y <= 800:
                    self.Player.position = (self.Player.position.x, new_y)

            if touch.location in self.laserButton.bbox:
                new_laser = SpriteNode('spc:LaserBlue6')
                new_laser.position = (self.Player.position.x, self.Player.position.y + 60)
                self.add_child(new_laser)
                self.lasers.append(new_laser)

        for l in self.lasers:
            l.position = (l.position.x, l.position.y + 7.5)
            if l.position.y > 800:
                l.remove_from_parent()
                self.lasers.remove(l)


if __name__ == '__main__':
    run(Game(), LANDSCAPE, show_fps=True)
BurntRice

Also, if you can add code for creating a scrolling background, that would be pretty good to (or adding a link to a previous forum).

JonB

The code that defines how the lasers move is here

l.position = (l.position.x, l.position.y + 7.5)

So, you can see that if you want a laser to move in a different direction,you will need another field to your lasers objects (might I suggest direction), which you would add to l.position. You might need to subclass SpriteNode,I forget if SpriteNodes let you add new attributes. if direction is a Vector2 object(or maybe Point2D, whatever class position is), you can simply add

l.position = l.position + l.direction

An alternative, if your laser is a "beam" instead of a circle, you could set rotation, then use rotation to both display the sprite, and decide which way to move - either with a bunch of if statements, or if you know trigonometry, you can use sin and cos of rotation (converted to radians) to compute the direction of motion.

Then, obviously, you need to change the code that spawns the laser to set a direction and/or rotation field, and also change the code that deles each laser when it falls off the screen, since you now need to check 3 or 4 edges, not just y.

BurntRice

Why won’t this work?

from scene import *
import turtle 
import ui 
import math
import random
A = Action()
direction = 1
#Setting up background colour for the entire scene
class Game(Scene):
    def setup(self):
        self.bg = SpriteNode('spc:BackgroundBlack')
        self.lasers = []

        #Creating the player
        self.Player = SpriteNode('shp:RoundRect')
        self.Player.color = ("cyan")
        self.Player.anchor_point = (0.5, 0)
        self.Player.position = (512, 400)
        self.add_child(self.Player)

        #Controlling the player
        self.UpCrtl = SpriteNode('iow:arrow_up_b_32')
        self.UpCrtl.x_scale = 3.5/1.0
        self.UpCrtl.y_scale = 3.5/1.0
        self.UpCrtl.alpha = 0.5
        self.UpCrtl.position = (175, 295)
        self.add_child(self.UpCrtl)

        self.DownCrtl = SpriteNode('iow:arrow_down_b_32')
        self.DownCrtl.x_scale = 3.5/1.0
        self.DownCrtl.y_scale = 3.5/1.0
        self.DownCrtl.alpha = 0.5
        self.DownCrtl.position = (175, 130)
        self.add_child(self.DownCrtl)

        self.RightCrtl = SpriteNode('iow:arrow_right_b_32')
        self.RightCrtl.x_scale = 3.5/1.0
        self.RightCrtl.y_scale = 3.5/1.0
        self.RightCrtl.alpha = 0.5
        self.RightCrtl.position = (250, 212.5)
        self.add_child(self.RightCrtl)

        self.LeftCrtl = SpriteNode('iow:arrow_left_b_32')
        self.LeftCrtl.x_scale = 3.5/1.0
        self.LeftCrtl.y_scale = 3.5/1.0
        self.LeftCrtl.alpha = 0.5
        self.LeftCrtl.position = (100, 212.5)
        self.add_child(self.LeftCrtl)

        #The button for shooting
        self.laserButton = SpriteNode('shp:Circle')
        self.laserButton.color = ('gray')
        self.laserButton.x_scale = 3/1
        self.laserButton.y_scale = 3/1
        self.add_child(self.laserButton)
        self.laserButton.position = (1000, 212.5)

    def update(self):
        for touch in self.touches.values():
            if touch.location in self.LeftCrtl.bbox:
                #left button pressed
                direction = 4
                new_x = self.Player.position.x - 5
                if new_x >= 0 and new_x <= 1100:
                    self.Player.position = (new_x, self.Player.position.y)

            if touch.location in self.RightCrtl.bbox:
                #right button pressed
                direction = 2
                new_x = self.Player.position.x + 5
                if new_x >= 0 and new_x <= 1100:
                    self.Player.position = (new_x, self.Player.position.y)

            if touch.location in self.UpCrtl.bbox:
                direction = 1
                new_y = self.Player.position.y + 5
                if new_y >= 0 and new_y <= 800:
                    self.Player.position = (self.Player.position.x, new_y)

            if touch.location in self.DownCrtl.bbox:
                direction = 3
                new_y = self.Player.position.y - 5
                if new_y >= 0 and new_y <= 800:
                    self.Player.position = (self.Player.position.x, new_y)

            if touch.location in self.laserButton.bbox:
                new_laser = SpriteNode('spc:LaserBlue6')
                new_laser.position=(self.Player.position.x,self.Player.position.y + 6)
                self.add_child(new_laser)
                self.lasers.append(new_laser)

            for l in self.lasers:
                if direction() = 1:
                l.position = (l.position.x, l.position.y + 75)
                if direction() = 2:
                    l.position = (l.position.x + 75, l.position.y)
                    if.direction() = 3:
                        l.position = (l.position.x, l.position.y - 75)
                        if direction() = 4:
                            l.position = (l.position.x + 75, l.position.y)

if __name__ == '__main__':
    run(Game(), LANDSCAPE, show_fps=True)
JonB

What do you think

if direction() = 1:

refers to? Where have you defined a function named direction? Are you attempting to compare the value of direction with 1, or assign the value 1 to direction?

Even if you fix the fundamental issues so that the code runs without error (this code likely is throwing Name error or maybe complaining about Callable, or AttributeError), it won't do what you want. Instead, it will make all lasers in the screen move in the direction you are holding the button.

Like I said, you either need to define a laser class that includes a direction at the time it is spawned, or you need to use an Action to move the laser (which you would spawn and run once, when the laser is created, with a move_to the final point on the screen, over a fixed time, in which case you don't need to manage the laser positions at all on your own)

ccc

if direction == 1: # use ==, not = !!!

BurntRice

Um, who is correct out of you two?
Your two comments kinda contradict each other.

ccc

@JonB 's question was: Should it be direction() as a function or direction as a variable? I suspect the latter so drop the ().

@ccc 's suggestion was: Use ==, not = to test equality in an if statement.

Both are important for correctness.

I would recommend that you run the Analyze (pyflakes) command accessible under the wrench menu. It flags exactly the same line of code that @JonB flagged above. Pyflakes is a subset of flake8 but it can still highlight some of these issues.

            for laser in self.lasers:
                if direction == 1:
                    laser.position.y += 75
                elif direction == 2:
                    laser.position.x += 75
                elif direction == 3:
                    laser.position.y -= 75
                elif direction == 4:
                    laser.position.x -= 75
JonB

@ccc unless he is trying to steer the lasers, your code won't work either. He needs to capture the direction that the ship is moving when the laser gets spawned, and keep track of that along with the laser object. Or use sprite rotation to define the direction of motion.

BurntRice

Have any of you actually tested the code or not?
I’ve got two half done codes for each scenario, so can you just quickly test it @ccc? I’m quite slow, and very busy with other work (got a deadline in the next 3 days, and I’m only half-way). If you get back to me then I can just focus on one strong effort.

JonB

Your code was untestable, because you provided code with syntax errors, that could not even run. You will probably get better results asking for help when it seems like you have even tried your code, and have made any attempt to diagnose it. If you are getting an error, you should mention that.

I did get your code to run, and behave in a logical way, as follows:
1) Moved the lasers update loop to the right indent level (you dont want to do that for every touch), and remived all of the if statements, since they were garbage and not properly indented
2) Used self.Player.rotation to define a persistent direction of last motion. Had you gotten through your syntax error, you would have noticed another immediate error, that direction was not defined, because of course, it is a local variable that only is defined during a touch, and so when you lift your finger off the direction button, it will fail. Presuamably what you want is for the direction buttons to rotate the player, and laser direction based on player rotation. Here it helps to define a player sprite that has some directionality, like one of the spc:PlayerShipxxx, but once you choose a sprite, you will use 0, math.pi/2, math.pi, and math.pi*3/2 as your four directions. You likely will want your anchor point to be 0.5,0.5. By trial and error, you will figure out which rotation is appropriate for each drirection.
3) Using player rotation now lets you set the laser rotation equal to Player.rotation. That takes care of drawing the laser properly.

4) I found it convienent to compute a Vector2
direction = Vector2(-math.sin(new_laser.rotation), math.cos(new_laser.rotation))
Depending in how you define your rotations, you might need to swap the sin and cos, and or change signs, if the laser moves in the wrong direction. how you define those rotations depend on the sprite you use for your player.
Then, you can simply offset the position by a scaled version of this,
l.position += direction*75
or
new_laser.position = self.Player.position + direction*6
etc
Vector2 is very useful like that. You could also use it to simplify your movement code in the same way

JonB

other comments:
You are spawning lasers at 60 per second when the button is touched, then moving them at 4500 pixels per second. Consider storing a variable that has the last time a laser was fired, then check self.t is greater than that time plus some lock out time. Or, use touch_began only as the method for firing lasers, rather than update (which gets called at 60 fps), so feverishly tapping the button can make lasers appear faster.

You can also use run_action with an Action.Sequence consisting of Action.move_by the laser by a few thousand pixels in the desired direction over some time that defines the speed, then removes it from the scene. The animation looks much smoother when you use Actions

BurntRice

Whenever, I press the laser button, the laser just goes to far left-hand side and goes up no matter what. I think its due to the fact that I haven’t implemented your tips correctly however.

from scene import *
import math
A = Action()


#Setting up background colour for the entire scene
class Game(Scene):

    def setup(self):
        self.bg = SpriteNode('spc:BackgroundBlack')
        self.lasers = []

        #Creating the player
        self.Player = SpriteNode('shp:RoundRect')
        self.Player.color = ("cyan")
        self.Player.anchor_point = (0.5, 0.5)
        self.Player.position = (512, 400)
        self.Player.rotation = 0.0
        self.add_child(self.Player)

        self.UpCrtl = SpriteNode('iow:arrow_up_b_32')
        self.UpCrtl.x_scale = 3.5/1.0
        self.UpCrtl.y_scale = 3.5/1.0
        self.UpCrtl.alpha = 0.5
        self.UpCrtl.position = (175, 295)
        self.add_child(self.UpCrtl)

        self.DownCrtl = SpriteNode('iow:arrow_down_b_32')
        self.DownCrtl.x_scale = 3.5/1.0
        self.DownCrtl.y_scale = 3.5/1.0
        self.DownCrtl.alpha = 0.5
        self.DownCrtl.position = (175, 130)
        self.add_child(self.DownCrtl)

        self.RightCrtl = SpriteNode('iow:arrow_right_b_32')
        self.RightCrtl.x_scale = 3.5/1.0
        self.RightCrtl.y_scale = 3.5/1.0
        self.RightCrtl.alpha = 0.5
        self.RightCrtl.position = (250, 212.5)
        self.add_child(self.RightCrtl)

        self.LeftCrtl = SpriteNode('iow:arrow_left_b_32')
        self.LeftCrtl.x_scale = 3.5/1.0
        self.LeftCrtl.y_scale = 3.5/1.0
        self.LeftCrtl.alpha = 0.5
        self.LeftCrtl.position = (100, 212.5)
        self.add_child(self.LeftCrtl)

        #The button for shooting
        self.laserButton = SpriteNode('shp:Circle')
        self.laserButton.color = ('gray')
        self.laserButton.x_scale = 3/1
        self.laserButton.y_scale = 3/1
        self.add_child(self.laserButton)
        self.laserButton.position = (1000, 212.5)

        #The score label
        self.score_label = LabelNode(text='Score: 0')
        self.score_label.anchor_point = (0, -300)
        self.score_label.position = (20, 718)
        self.score_label.font = ('Arial Rounded MT Bold', 30)
        self.add_child(self.score_label)


    #Movement code.
    def update(self):
        for touch in self.touches.values():
            if touch.location in self.LeftCrtl.bbox:
                new_x = self.Player.position.x - 5
                self.Player.rotation = math.pi*3/2
                if new_x >= 0 and new_x <= 1100:
                    self.Player.position = (new_x, self.Player.position.y)

            if touch.location in self.RightCrtl.bbox: 
                new_x = self.Player.position.x + 5
                self.Player.rotation = math.pi/2
                if new_x >= 0 and new_x <= 1100:
                    self.Player.position = (new_x, self.Player.position.y)

            if touch.location in self.UpCrtl.bbox:
                new_y = self.Player.position.y + 5
                self.Player.rotation = 0.0
                if new_y >= 0 and new_y <= 800:
                    self.Player.position = (self.Player.position.x, new_y)

            if touch.location in self.DownCrtl.bbox: 
                new_y = self.Player.position.y - 5
                self.Player.rotation = math.pi
                if new_y >= 0 and new_y <= 800:
                    self.Player.position = (self.Player.position.x, new_y)

            if touch.location in self.laserButton.bbox:
                new_laser = SpriteNode('spc:LaserBlue6')
                direction = Vector2(-math.sin(new_laser.rotation), math.cos(new_laser.rotation))
                self.add_child(new_laser)
                self.lasers.append(new_laser)

                for l in self.lasers:
                    l.position += direction*75


if __name__ == '__main__':
    run(Game(), LANDSCAPE, show_fps=True)
ccc

random, turtle, and ui are imported but not used which slows down startup and uses more memory.

A = Action() is created but not used which slows down startup and uses more memory.

self.laserButton = SpriteNode('shp:Circle') is created twice which slows down startup, uses more memory, and makes it confusing which instance is being clicked on.

Please reread and follow @JonB advise above.

JonB

If you want help, you need to explain what the problem is, what you have tried. For instance you might start by saying "help, my lasers now don't even spawn in the right position" to which someone might ask (maybe even you), gee, did you remember to set the laser position when you spawned it?

Or you might be wondering why the laser w only move when you are touching the button. In which case one could respond that you maybe didn't fix the indentation as previously mentioned. The act of typing out "for some reason my lasers only move when I am touching the fire button" might make you look at the code that moves your lasers, or look at the code that runs when the fire button is pressed.

You might be wondering "hey, I updated the code to use direction, but all lasers seem to use the direction from when the button was pressed", which would allow someone to point out that if you want to use it in your laser update loop, it needs to be defined inside said loop (using the rotation of the looped laser object), or store it as an attribute of the laser object.

I would encourage you to try to understand your own code before asking for help. Then you can be specific about what you tried, what debugging you added in to understand the program flow, or what you think should be happening vs what is actually happening.

BurntRice

Whenever I press the shoot button, lasers fire down on the far left-hand side of the screen, and only go up. I am not getting any immediate errors, so I am investigating into the math.

BurntRice

I think its due to the fact that I haven’t implemented your tips correctly however.

BurntRice

I have assigned so the new_laser.rotation is = to the player rotation, this results in lasers popping on sides, not the right sides because I probably have to switch the math.pi equations around, but they are not moving.

JonB

Move the for l in self.lasers loop to the proper indentation level. This should not be inside any of the touch handling code.

You will then use the l.rotation to set the motion direction.

Also, you will eventually need code that deletes lasers once they are off screen ( in that loop, check if laser.bbox is in self.bbox)

BurntRice

Um, sorry? I don’t really understand. I have properly indented the for l in self.lasers part, I don’t know how to add l.rotation to set motion direction.

JonB

Post your code. The last version you posted had the laser motion code INSIDE the touch handling code, which means the code would only execute when the fire button was being pressed.

BurntRice
from scene import *
import math
A = Action()


#Setting up background colour for the entire scene
class Game(Scene):

    def setup(self):
        self.bg = SpriteNode('spc:BackgroundBlack')
        self.lasers = []

        #Creating the player
        self.Player = SpriteNode('shp:RoundRect')
        self.Player.color = ("cyan")
        self.Player.anchor_point = (0.5, 0.5)
        self.Player.position = (512, 400)
        self.Player.rotation = 0.0
        self.add_child(self.Player)

        self.UpCrtl = SpriteNode('iow:arrow_up_b_32')
        self.UpCrtl.x_scale = 3.5/1.0
        self.UpCrtl.y_scale = 3.5/1.0
        self.UpCrtl.alpha = 0.5
        self.UpCrtl.position = (175, 295)
        self.add_child(self.UpCrtl)

        self.DownCrtl = SpriteNode('iow:arrow_down_b_32')
        self.DownCrtl.x_scale = 3.5/1.0
        self.DownCrtl.y_scale = 3.5/1.0
        self.DownCrtl.alpha = 0.5
        self.DownCrtl.position = (175, 130)
        self.add_child(self.DownCrtl)

        self.RightCrtl = SpriteNode('iow:arrow_right_b_32')
        self.RightCrtl.x_scale = 3.5/1.0
        self.RightCrtl.y_scale = 3.5/1.0
        self.RightCrtl.alpha = 0.5
        self.RightCrtl.position = (250, 212.5)
        self.add_child(self.RightCrtl)

        self.LeftCrtl = SpriteNode('iow:arrow_left_b_32')
        self.LeftCrtl.x_scale = 3.5/1.0
        self.LeftCrtl.y_scale = 3.5/1.0
        self.LeftCrtl.alpha = 0.5
        self.LeftCrtl.position = (100, 212.5)
        self.add_child(self.LeftCrtl)

        #The button for shooting
        self.laserButton = SpriteNode('shp:Circle')
        self.laserButton.color = ('gray')
        self.laserButton.x_scale = 3/1
        self.laserButton.y_scale = 3/1
        self.add_child(self.laserButton)
        self.laserButton.position = (1000, 212.5)

        #The score label
        self.score_label = LabelNode(text='Score: 0')
        self.score_label.anchor_point = (0, 0)
        self.score_label.position = (10, 790)
        self.score_label.font = ('Arial Rounded MT Bold', 30)
        self.add_child(self.score_label)

    #Movement code.
    def update(self):
        for touch in self.touches.values():
            if touch.location in self.LeftCrtl.bbox:
                new_x = self.Player.position.x - 5
                self.Player.rotation = math.pi*3/2
                if new_x >= 0 and new_x <= 1100:
                    self.Player.position = (new_x, self.Player.position.y)

            if touch.location in self.RightCrtl.bbox: 
                new_x = self.Player.position.x + 5
                self.Player.rotation = math.pi/2
                if new_x >= 0 and new_x <= 1100:
                    self.Player.position = (new_x, self.Player.position.y)

            if touch.location in self.UpCrtl.bbox:
                new_y = self.Player.position.y + 5
                self.Player.rotation = 0.0
                if new_y >= 0 and new_y <= 800:
                    self.Player.position = (self.Player.position.x, new_y)

            if touch.location in self.DownCrtl.bbox: 
                new_y = self.Player.position.y - 5
                self.Player.rotation = math.pi
                if new_y >= 0 and new_y <= 800:
                    self.Player.position = (self.Player.position.x, new_y)

            if touch.location in self.laserButton.bbox:
                new_laser = SpriteNode('spc:LaserBlue6')
                new_laser.rotation = self.Player.rotation
                direction = Vector2(-math.sin(new_laser.rotation), math.cos(new_laser.rotation))
                self.add_child(new_laser)
                self.lasers.append(new_laser)

            for l in self.lasers:
                l.position += direction*75


if __name__ == '__main__':
    run(Game(), LANDSCAPE, show_fps=True)
ccc

Can

            if touch.location in self.DownCrtl.bbox: 
                new_y = self.Player.position.y - 5
                self.Player.rotation = math.pi
                if new_y >= 0 and new_y <= 800:
                    self.Player.position = (self.Player.position.x, new_y)

be simplified to

            if touch.location in self.DownCrtl.bbox:
                self.Player.rotation = math.pi
                if 0 <= new_y <= 800:
                    self.Player.position.y -= 5
JonB

@BurntRice okay, so

l.position += direction*75

Let's think about this for a minute. Where have you defined direction? Remember, the goal here in the movement code is to figure out the direction for EACH laser that you are moving. So, you need to figure out the direction Vector2 based on each laser's rotation.

ccc

Perhaps

            for l in self.lasers:
                l.position += direction*75

needs to be indented under the if touch.location in self.laserButton.bbox: block where direction is defined.

JonB

No. Trying the Socratic method to get him to think about his own code...

This would be simpler if the touch code and laser update code were their own functions, as they are completely independent. The fact that he shares a direction variable name is what is confusing him.

The touch code creates a laser. The laser created is spawned, and the code that sets the rotation and initial offset.

The code that moves the lasers is completely separate (one needn't have touch code at all to move laser objects). Lasers move even when buttons are not touched. So, how can you figure out what direction a given laser should travel?

BurntRice

I’ve made it so my laser shoots in the direction that I want it to, but, they are not moving. Once one laser pops up, the next one goes 75 pixels ahead, for ad infinitum. I don’t know if this is because of where I indented my direction value, but I don’t think so as that is mostly for rotation; nothing about moving forward etc.

JonB

So you have code which is moving your laser, but it is only running once?

Go back and look at all of your code to see if your indentation is correct. If you want laser movement code to run every update cycle, and it isn't, there must be some control code (if, for, with, while, etc ) that is preventing it from running.

What will help you in this case is to break your update function into functions that you call, so that you can better follow the program flow. Once an indented section of code gets to be longer than a screenfull, that's a good indication that you might need a function.

For instance, if each button had it's own method. Or, if all of your touch handling had it's own method. Then your update method would be easier for you to follow:

def update (self):
   handleTouches(self)
   moveLasers(self)
   checkCollisions(self)
   spawnEnemies(self)
BurntRice

I have been looking for quite a bit, and I’m really blind, but I can’t find any indentation errors.

from scene import *
import math
A = Action()


#Setting up background colour for the entire scene
class Game(Scene):

    def setup(self):
        self.bg = SpriteNode('spc:BackgroundBlack')
        self.lasers = []

        #Creating the player
        self.Player = SpriteNode('spc:Fire1')
        self.Player.color = ("cyan")
        self.Player.anchor_point = (0.5, 0.5)
        self.Player.x_scale = 2/1.0
        self.Player.y_scale = 1.5/1.0
        self.Player.position = (512, 400)
        self.Player.rotation = 0.0
        self.add_child(self.Player)

        self.UpCrtl = SpriteNode('iow:arrow_up_b_32')
        self.UpCrtl.x_scale = 3.5/1.0
        self.UpCrtl.y_scale = 3.5/1.0
        self.UpCrtl.alpha = 0.5
        self.UpCrtl.position = (175, 295)
        self.add_child(self.UpCrtl)

        self.DownCrtl = SpriteNode('iow:arrow_down_b_32')
        self.DownCrtl.x_scale = 3.5/1.0
        self.DownCrtl.y_scale = 3.5/1.0
        self.DownCrtl.alpha = 0.5
        self.DownCrtl.position = (175, 130)
        self.add_child(self.DownCrtl)

        self.RightCrtl = SpriteNode('iow:arrow_right_b_32')
        self.RightCrtl.x_scale = 3.5/1.0
        self.RightCrtl.y_scale = 3.5/1.0
        self.RightCrtl.alpha = 0.5
        self.RightCrtl.position = (250, 212.5)
        self.add_child(self.RightCrtl)

        self.LeftCrtl = SpriteNode('iow:arrow_left_b_32')
        self.LeftCrtl.x_scale = 3.5/1.0
        self.LeftCrtl.y_scale = 3.5/1.0
        self.LeftCrtl.alpha = 0.5
        self.LeftCrtl.position = (100, 212.5)
        self.add_child(self.LeftCrtl)

        #The button for shooting
        self.laserButton = SpriteNode('shp:Circle')
        self.laserButton.color = ('gray')
        self.laserButton.x_scale = 3/1
        self.laserButton.y_scale = 3/1
        self.add_child(self.laserButton)
        self.laserButton.position = (1000, 212.5)

        #The score label
        self.score_label = LabelNode(text='Score: 0')
        self.score_label.anchor_point = (0, 0)
        self.score_label.position = (10, 790)
        self.score_label.font = ('Arial Rounded MT Bold', 30)
        self.add_child(self.score_label)

    #Movement code.
    def update(self):
        for touch in self.touches.values():
            if touch.location in self.LeftCrtl.bbox:
                new_x = self.Player.position.x - 5
                self.Player.rotation = math.pi/2
                if new_x >= 0 and new_x <= 1100:
                    self.Player.position = (new_x, self.Player.position.y)

            if touch.location in self.RightCrtl.bbox:
                new_x = self.Player.position.x + 5
                self.Player.rotation = math.pi*3/2
                if new_x >= 0 and new_x <= 1100:
                    self.Player.position = (new_x, self.Player.position.y)

            if touch.location in self.UpCrtl.bbox:
                new_y = self.Player.position.y + 5
                self.Player.rotation = 0.0
                if new_y >= 0 and new_y <= 800:
                    self.Player.position = (self.Player.position.x, new_y)

            if touch.location in self.DownCrtl.bbox:
                new_y = self.Player.position.y - 5
                self.Player.rotation = math.pi
                if new_y >= 0 and new_y <= 800:
                    self.Player.position = (self.Player.position.x, new_y)

            if touch.location in self.laserButton.bbox:
                new_laser = SpriteNode('shp:Circle')
                new_laser.x_scale = 0.25/1.0
                new_laser.y_scale = 0.25/1.0
                new_laser.position = self.Player.position
                new_laser.rotation = self.Player.rotation
                direction = Vector2(-math.sin(new_laser.rotation), math.cos(new_laser.rotation))
                self.add_child(new_laser)
                self.lasers.append(new_laser)
                for l in self.lasers:
                    l.position += direction*75


if __name__ == '__main__':
    run(Game(), LANDSCAPE, show_fps=True)
JonB

Think about what should happen when you lifr your finger. Remember, update gets called 60 times per second.

Do you want the existing lasers to keep moving? Or move only when your finger is down, and freeze when your finger is up.

I would suggest rewriting your update function as follows, to make it easier for you to follow:

   def update(self):
      self.handle_button_presses()
      self.move_lasers()

Your touch handling code then moves to one method, and other game logic moves to their own functions, and get called from update. For example, eventually you might be moving enemies or asteroids or the map. You might be spawning enemies at certain intervals, which would also be it's own method. Thus, you would create a spawn_enemies, and move_enemies methods, maybe a scroll_map if this is scroller type game, perhaps a check_collisions which checks if the player hit any obstacles or enemies, etc. You should be able to write your entire update method using dummy methods that sort of lay out what you need to work in, and can implement the methods one at a time, since they should all be mostly independent of each other, just depending on a common set of attributes or positions.

BurntRice

I want my existing lasers to keep on moving. I have also implemented your idea of rewriting my update function.
I really don’t know how to though. If I use a touch.began method, it runs once, but stays in it’s place.
Am I forgetting to add something?

JonB

Okay, let's try this again.

If you want your laser to move, the movement code must be called EVERY time update is called. There should be no if statements around the for loop that moves the lasers.

In your old code the laser loop was inside the if statement that checked for the laser button touch. That's why it doesn't move when your finger is up. You will need to update your loop because the direction variable that you use for moving the lasers must be define inside the loop, because it can be different for every laser. You'll need to grab the laser rotation for each laser to figure that out.

Post your new updated 5 line update method. And post your update_laser_postions function or whatever you called it.

BurntRice

def update(self):
self.handle_button_presses()
self.move_lasers()
if random.random() < 0.01:
self.spawn_item()

def handle_button_presses(self):
    for touch in self.touches.values():
        if touch.location in self.LeftCrtl.bbox:
            new_x = self.Player.position.x - 5
            self.Player.rotation = math.pi/2
            if new_x >= 0 and new_x <= 1100:
                self.Player.position = (new_x, self.Player.position.y)

        if touch.location in self.RightCrtl.bbox:
            new_x = self.Player.position.x + 5
            self.Player.rotation = math.pi*3/2
            if new_x >= 0 and new_x <= 1100:
                self.Player.position = (new_x, self.Player.position.y)

        if touch.location in self.UpCrtl.bbox:
            new_y = self.Player.position.y + 5
            self.Player.rotation = 0.0
            if new_y >= 0 and new_y <= 800:
                self.Player.position = (self.Player.position.x, new_y)

        if touch.location in self.DownCrtl.bbox:
            new_y = self.Player.position.y - 5
            self.Player.rotation = math.pi
            if new_y >= 0 and new_y <= 800:
                self.Player.position = (self.Player.position.x, new_y)

def move_lasers(self):
    for touch in self.touches.values():
        if touch.location in self.laserButton.bbox:
            new_laser = SpriteNode('shp:Circle')
            new_laser.x_scale = 0.25/1.0
            new_laser.y_scale = 0.25/1.0
            new_laser.position = self.Player.position
            new_laser.rotation = self.Player.rotation
            direction = Vector2(-math.sin(new_laser.rotation), math.cos(new_laser.rotation))
            self.add_child(new_laser)
            self.lasers.append(new_laser)
            for l in self.lasers:
                l.position += direction*75
dereq

Tic Tac Toe is best online playing game.Tic Tac Toe is recognized as X & O or you may call it Noughts and Crosses Game.You can challenge Paper Man to play Tic Tac Toe game or play it with other online player.

JonB
for l in self.lasers:
                l.position += direction*75

That is the code that moves a laser beam, right?

In words, can you explain under what conditions this code runs?

Does that code execute EVERY time update is called, or is there some logic that controls when it will run?

Hint, look above it for any for loops or if statements.

arsartindustry

thanks you

BurntRice

The code runs whenever the laser button detects that someone has touched it.

JonB

@BurntRice but you told me that you want lasers to MOVE even when someone is not currently touching the button.

So, you must move the MOVE code out from inside the for touch in self.touches loop.

Your code has two parts:
* Create a new laser object when person touches the button
* Move all of the existing lasers

The first part should only happen when user is touching the button.

The second part should happen always.

Erase your for l in self.lasers code. Now, write new movement code inside a new function, which you will call from update. You will need to redefine direction inside your loop -- it is not the same direction variable that is used inside your creation code. for now, hardcod direction=Vector2(0,1) just so you can get something to work. Later you will use the laser rotation to determine direction.

BurntRice

I’m still working on it, but I added this:

def update_lasers(self):
        for l in self.lasers:
            l.direction = Vector2(0,1)
            l.rotation = self.Player.rotation
            l.position = l.position + l.direction

I just want to check if I’m getting side tracked or not.

JonB

When a laser is moving, it should move based on IT's rotation,not the player rotation. You might move after the laser gets fired, but you want the laser to go straight.

So delete the line updating the rotation. The rotation was set when you spawn the laser.

This code should work, but of course the laser always goes in one direction. So next, you can redefine direction using the l.rotation, similar to the way you calculated it when you spawn the laser

BurntRice

I think I’ve finally done it! It works, as it shoots in the way I want it, and keeps on travelling even when I haven’t touched it, thanks.
All I have to do is add a action so I can’t spam or hold down on the button. What is the best way to add this?

BurntRice

Also, I would also like to know how to code screen wrapping.

ccc
player.location.x %= screen.width
player.location.y %= screen.height
BurntRice

Oh, thanks.

BurntRice

Wait, how do find the screen width and height? And what about location?

cvp

@BurntRice said:

how do find the screen width and height

ui.get_screen_size() --> width,height

JonB

Re spamming, one approach is, in your button handling code, to keep track of self.t, and self.lastfire_time. When current time minus last fire time is > some value, reset the last fire time to current time, and fire a laser. Otherwise, pass

BTW, in your laser loop, you will need to check if the laser is off screen (not in self.bounds), then delete the object from the scene. Otherwise your scene will eventually bog down or crash because it is moving millions of laser objects.

This could also be done in your check_collisions method where you see if your lasers hit your enemies.

BurntRice

@cvp And what about location?

JonB

position, not location.

If you want to wrap, you need to fundamentally change your button logic, since you check limits

BurntRice

I have added this:
width, height = ui.get_screen_size()
And then added this later:
if self.Player.position.x <= 0 or self.Player.position.x >= 1112:
self.Player.position.x %= width
if self.Player.position.y <= 0 or self.Player.position.y >= 834:
self.Player.position.y %= height
This didn’t work. I checked if I had gotten the width and height variables in a seperate script, and I did. I am now stumped.

JonB

What do you think your if statement does?

BurntRice

Whenever the Players position.x is equal to or smaller than 0 or whenever the position.x is equal to or greater than 1112, you do the remainder of the division of the screen height.

JonB

Post your button handling code again... You used to have checks that the new position was in the screen.

You actually don't need the if statements --
You could say player.position.x%=self.width, which will always cause it to wrap around.

BurntRice

Here you go:

def handle_button_presses(self):
        rotationAdd = math.pi/180
        self.Player.location = self.Player.position
        for touch in self.touches.values():
            if touch.location in self.LeftCrtl.bbox:
                actions = [A.rotate_by(math.pi/180)]
                self.Player.rotation = self.Player.rotation + rotationAdd
                self.Player.run_action(A.sequence(actions))

            if touch.location in self.RightCrtl.bbox:
                actions = [A.rotate_by(math.pi/-180)]
                self.Player.rotation = self.Player.rotation - rotationAdd
                self.Player.run_action(A.sequence(actions))


            if touch.location in self.UpCrtl.bbox:
                direction = Vector2(-math.sin(self.Player.rotation), math.cos(self.Player.rotation))
                self.Player.position += direction*3
                if self.Player.position.x <= 0 or self.Player.position.x >= 1112:
                    self.Player.position.x %= width
                if self.Player.position.y <= 0 or self.Player.position.y >= 834:
                    self.Player.position.y %= height

I did change the movement code a little.

ccc

@BurntRice said:

            if self.Player.position.x <= 0 or self.Player.position.x >= 1112:
                self.Player.position.x %= width
            if self.Player.position.y <= 0 or self.Player.position.y >= 834:
                self.Player.position.y %= height

@JonB said:

You actually don't need the if statements

            self.Player.position.x %= width
            self.Player.position.y %= height
JonB

Is width an height actually defined someplace?

What isn't working in your code? Is your player rotating and moving as expected? Is only the wrapping broken?

BurntRice

Only the wrapping.

BurntRice

width, height = ui.get_screen_size()
I think this is right. I did a seperate script where I print it out inside the console, and it worked.

BurntRice

I tried placing it self.update, but then it wouldn’t work.

ccc

Can we please get the code full listing? Please use the ` icon above the post edit box to paste your code so it is formatted for this forum. \``
Insert Code Here
```

BurntRice
from scene import *
import math
import random
import sound
import ui
A = Action()
width, height = ui.get_screen_size()
#Setting up meteorites


#Setting up background colour for the entire scene
class Game(Scene):

    def setup(self):
        self.bg = SpriteNode('spc:BackgroundBlack')
        self.lasers = []
        self.items = []
        self.LaserTime = 0
        self.frame_counter = 0
        self.meteor = []

        #Creating the player
        self.Player = SpriteNode('iow:arrow_up_b_32')
        self.Player.color = (1.0, 1.0, 1.0)
        self.Player.anchor_point = (0.5, 0.5)
        self.Player.x_scale = 2/1.0
        self.Player.y_scale = 3/1.0
        self.Player.position = (512, 400)
        self.Player.rotation = 0.0
        self.add_child(self.Player)

        self.UpCrtl = SpriteNode('iow:arrow_up_b_32')
        self.UpCrtl.x_scale = 3.5/1.0
        self.UpCrtl.y_scale = 3.5/1.0
        self.UpCrtl.alpha = 0.5
        self.UpCrtl.position = (175, 295)
        self.add_child(self.UpCrtl)

        self.RightCrtl = SpriteNode('iow:arrow_right_b_32')
        self.RightCrtl.x_scale = 3.5/1.0
        self.RightCrtl.y_scale = 3.5/1.0
        self.RightCrtl.alpha = 0.5
        self.RightCrtl.position = (250, 212.5)
        self.add_child(self.RightCrtl)

        self.LeftCrtl = SpriteNode('iow:arrow_left_b_32')
        self.LeftCrtl.x_scale = 3.5/1.0
        self.LeftCrtl.y_scale = 3.5/1.0
        self.LeftCrtl.alpha = 0.5
        self.LeftCrtl.position = (100, 212.5)
        self.add_child(self.LeftCrtl)

        #The button for shooting
        self.laserButton = SpriteNode('shp:Circle')
        self.laserButton.color = ('gray')
        self.laserButton.x_scale = 3/1
        self.laserButton.y_scale = 3/1
        self.add_child(self.laserButton)
        self.laserButton.position = (1000, 212.5)

        #The score label
        self.score_label = LabelNode(text='Score: 0')
        self.score_label.anchor_point = (0, 0)
        self.score_label.position = (10, 790)
        self.score_label.font = ('Joystix', 30)
        self.add_child(self.score_label)

    #Movement code.
    def update(self):
        self.handle_button_presses()
        self.move_lasers()
        self.update_lasers()
        self.spawn_meteor()
        width, height = ui.get_screen_size()
        self.LaserTime = self.LaserTime + 1

    def handle_button_presses(self):
        rotationAdd = math.pi/180
        self.Player.location = self.Player.position
        for touch in self.touches.values():
            if touch.location in self.LeftCrtl.bbox:
                actions = [A.rotate_by(math.pi/180)]
                self.Player.rotation = self.Player.rotation + rotationAdd
                self.Player.run_action(A.sequence(actions))

            if touch.location in self.RightCrtl.bbox:
                actions = [A.rotate_by(math.pi/-180)]
                self.Player.rotation = self.Player.rotation - rotationAdd
                self.Player.run_action(A.sequence(actions))

            if touch.location in self.UpCrtl.bbox:
                direction = Vector2(-math.sin(self.Player.rotation), math.cos(self.Player.rotation))
                self.Player.position += direction*3 
                self.Player.position.x %= width
                self.Player.position.y %= height

    def move_lasers(self):
        for touch in self.touches.values():
            if touch.location in self.laserButton.bbox and self.LaserTime >= 15:
                #reset the time
                self.LaserTime = 0
                new_laser = SpriteNode('shp:Circle')
                new_laser.x_scale = 0.3/1.0
                new_laser.y_scale = 0.3/1.0
                new_laser.position = self.Player.position
                new_laser.rotation = self.Player.rotation
                self.add_child(new_laser)
                self.lasers.append(new_laser)
                sound.play_effect('arcade:Laser_6')

    def update_lasers(self):
        for l in self.lasers:
            l.direction = Vector2(-math.sin(l.rotation), math.cos(l.rotation))*10
            l.position += l.direction
            if l.position.x < 0:
                l.remove_from_parent()
                self.lasers.remove(l)
            if l.position.x > 1112:
                l.remove_from_parent()
                self.lasers.remove(l)
            if l.position.y < 0:
                l.remove_from_parent()
                self.lasers.remove(l)
            if l.position.y > 834:
                l.remove_from_parent()
                self.lasers.remove(l)

    def spawn_meteor(self):
        self.frame_counter = self.frame_counter + 1
        if self.frame_counter >= 60:
            self.frame_counter = 0
            new_meteor = SpriteNode('spc:MeteorGraySmall1')
            new_meteor.position = (random.randint(0, 1024), 834)
            self.add_child(new_meteor)
            self.meteor.append(new_meteor)

        for meteor in self.meteor:
            meteor.position = (meteor.position.x, meteor.position.y - 5)
            if meteor.position.y < - 100:
                meteor.remove_from_parent()
                self.meteor.remove(meteor)


if __name__ == '__main__':
    run(Game(), LANDSCAPE, show_fps=True)
ccc

So, (as is often the case...) I was wrong about how to do the wrap around when the player gets to the edge of the screen.

self.Player.position.x %= width
self.Player.position.y %= height

# must be changed to...

self.Player.position = (self.Player.position.x % self.size.w, self.Player.position.y % self.size.h)

This is because position is a scene.Point which is scene.Vector2 which behaves more like a tuple than a list.

Also, you can safely delete all lines containing self.Player.location.

cvp

@ccc said:

as is often the case

Not so True 😂

ccc

update_lasers() can be simplified...

    def update_lasers(self):
        for laser in self.lasers:
            laser.direction = Vector2(-math.sin(laser.rotation), math.cos(laser.rotation))*10
            laser.position += laser.direction
            if laser.position not in self.bounds:  # laser has gone off the screen
                laser.remove_from_parent()
                self.lasers.remove(laser)
JonB

Also, it seems to me that you don't need both an Action that sets rotation and the actual assignment to rotation.. If you want to simulate acceleration, then you need to keep track of a velocity, and pressing the arrow button changes the velocity, and then the rotation is set by adding velocity.

moezop

press the laser button, the laser just goes to far left-hand side and goes up no matter what.. .