xTalk Graphic Action Games Step By Step

A forum to share your demonstrations stacks, fun stacks, games, etc.
Post Reply
xAction
Posts: 285
Joined: Thu Sep 16, 2021 1:40 pm
Contact:

xTalk Graphic Action Games Step By Step

Post by xAction »

Rotate_Triangle.oxtStack
(3.73 KiB) Downloaded 73 times
RotateTriangle_v001.gif
RotateTriangle_v001.gif (40.79 KiB) Viewed 920 times
I will divulge some basic game programming principles, as simply as I can.
A full game gets complicated and thick with scripts very fast so hopefully this gives beginners the pieces of the puzzle to work with. "Action Games" implies moving objects, as opposed to say a turn based strategy game, or RPG.

I am going to stick to 'graphics' objects as they keep us out of the file system and/or image data and they give good retro vibes.

Code posted here is just for the sake of following along if you are a beginner and need to know how it all comes together, the attached stack provides the code if you need to get into it.

Step 1. Define the triangle "Ship"
We might reuse this handler for many ships later. Probably with a different starting location
Note we delete any previous graphic of the same name as clones can result in unexpected behavior.

Code: Select all

on defineGraphicTriangle tName
   if there is a graphic tName then delete graphic tName
   set the style of the templateGraphic to "polygon"
   set the points of the templategraphic to "0,0" &cr& "-120,60" &cr& "-120,-60" &cr& "0,0"
   set the lineSize of the templateGraphic to 1
   set the visible of the templateGraphic to true
   set the opaque of the templateGraphic to false
   set the name of the templateGraphic to tName 
   create graphic tName
   set the location of graphic  tName to width of this stack/2 , height of this stack /2
end defineGraphicTriangle
Step 2. Make sure the graphic is created when we need it.
For simplicity we do it here in the scrollbar, in a game you'll have some 'init' scripts to manage this.

Code: Select all

on mouseEnter
   --- ensure that there is a graphic to manipulate. Assuming you copied this script or scrollbar to a new stack
   if there is not a graphic "ship" then defineGraphicTriangle "ship"
end mouseEnter
Step 3. Define the rotate triangle polygon function
The 'regular' style polygon graphics used by the IDE can be rotated with the built-in 'angle' command, but 'polygon' style polygon graphics need the calculations like you see here in rotateTrianglePolygon as they may not be of purely geometrically generated points. I hope I stated that clear enough.

Code: Select all

function rotateTrianglePolygon pPoints, pAngle
   --- pPoints: A list of points (x1,y1,x2,y2,x3,y3) representing the triangle vertices.
   --- pAngle: The angle in degrees by which to rotate the triangle.
   
   put the number of items in pPoints into numPoints
   if numPoints <> 6 then
      answer "Please provide exactly three pairs of coordinates (x1,y1,x2,y2,x3,y3)."
      exit to top
   end if
   
   --- Convert angle to radians
   put pAngle into radians
   divide radians by 180
   multiply radians by pi
   
   --- Extract individual coordinates
   put item 1 of pPoints into x1
   put item 2 of pPoints into y1
   put item 3 of pPoints into x2
   put item 4 of pPoints into y2
   put item 5 of pPoints into x3
   put item 6 of pPoints into y3
   
   --- Rotate each point
   put (x1 * cos(radians)) - (y1 * sin(radians)) into newX1
   put (x1 * sin(radians)) + (y1 * cos(radians)) into newY1
   
   put (x2 * cos(radians)) - (y2 * sin(radians)) into newX2
   put (x2 * sin(radians)) + (y2 * cos(radians)) into newY2
   
   put (x3 * cos(radians)) - (y3 * sin(radians)) into newX3
   put (x3 * sin(radians)) + (y3 * cos(radians)) into newY3
   
   -- Return the rotated coordinates
   return newX1 & "," & newY1 & "," & newX2 & "," & newY2 & "," & newX3 & "," & newY3
end rotateTrianglePolygon
Step 4. Rotate the angle of the graphic to the value of the thumb position of the scrollbar

Code: Select all

on scrollBarDrag
   put "0,0" & comma & "-120,60" & comma & "-120,-60" into tPoints
   --- rotate the points of the graphic
   put rotateTrianglePolygon(tPoints,the thumbposition of me) into tNewPoints
   --- add first line of the points after last line to close the graphic
   put cr & line 1 of tNewPoints after tNewPoints
   --- store the original location of the ship
   put the loc of graphic "ship" into tLoc
   --- apply the new rotated points
   set the points of graphic "ship" to tNewPoints
   --- reset the ship to the original location
   set the loc of graphic "ship" to tLoc
end scrollBarDrag
Yes, I know it doesn't look like much but it's a start.
xAction
Posts: 285
Joined: Thu Sep 16, 2021 1:40 pm
Contact:

xTalk Graphic Action Games Step By Step : Rotate To Mouse

Post by xAction »

Rotate_Triangle_Aim_At_Mouse.oxtStack
(6.29 KiB) Downloaded 65 times
RotateTriangle_point_At_Mouse.gif
RotateTriangle_point_At_Mouse.gif (26.63 KiB) Viewed 919 times
This part looks just like the last part, I will post what specifically changed here as the foundation has been laid above.
Triangle 'ship' is half size so there's room to mouse around the stack window.
Moved the script from the scrollbar to the stack because we'll want ubiquitous access to the functions.
The scrollbar script has been reduced to simply:

Code: Select all

--- the thumbosition of the scrollbar = the angle the triangle will face
on scrollBarDrag
   SetTrianglePolygonAngle (the thumbposition of me),"ship"
end scrollBarDrag
Dragging the scrollbar calls SetTrianglePolygonAngle with the thumb position and an object name.
Now we can spin as many ships as we create.

Code: Select all

on setTrianglePolygonAngle tAngle,tName
   --- make sure we have a 'ship' to rotate, assuming you simply copied the scripts to a new stack
   if there is not a graphic tName then defineGraphicTriangle tName
   
   --- define the original points of the graphic  triangle
   put "0,0" & comma & "-60,30" & comma & "-60,-30" into tPoints
   
   --- rotate the points to the angle
   put rotateTrianglePolygon(tPoints,tAngle) into tNewPoints
   
   --- add line 1 of the points after last line of the points to close the graphic
   put cr & line 1 of tNewPoints after tNewPoints
   
   --- memorize the current location of 'ship'
   put the loc of graphic tName into tLoc
   
   --- set the points of the graphic to the rotated points
   set the points of graphic tName to tNewPoints
   
   --- reset the location of the ship to it's original location
   set the loc of graphic tName to tLoc
end setTrianglePolygonAngle
The stack script has been modified to allow the angle of the ship graphic to continuously target the mouse location as long as it is in the stack window and we are not in 'developer mode' if after choosing some IDE tool other than the 'pointer tool'.

Step 1. Track the mouse
Note:
'if the mouseloc is within the rect of this stack'
does not return the same result as
if the mouseloc is within 0, 0, width of this stack, height of this stack
The first one returns values based on the window's offset within the screen, but our request for the mouse location is within the specific rectangle we are focusing our attention on, which starts at 0,0.

Code: Select all

on mouseMove
   --- exit if we are using the scrollbar control  or things get messy
   if  the mouseloc is within the rect of scrollbar "angle"  then exit mouseMove
   --- exit if the mouse is not within the actual rect of the stack which is not the same as "rect of this stack", go figure
   if the mouseloc is not within  0,0,width of this stack,height of this stack then exit mouseMove
   --- only react if we are not in developer mode 
   if the tool is "browse tool"  then  FaceTheMouseLoc
end mouseMove
Step 2. Calculate the angle in degrees from the current coordinate to the target coordinate ie, source to target

Code: Select all

--- Calculate the angle in degrees from the current coordinate to the target coordinate  ie, source to target
function calculateAngle pCurrentX, pCurrentY, pTargetX, pTargetY
   put pTargetX - pCurrentX into deltaX
   put pTargetY - pCurrentY into deltaY
   put atan2(deltaY, deltaX) into radiansAngle
   put radiansAngle * (180 / pi) into degreesAngle
   return degreesAngle
end calculateAngle
Step 3. Face The Mouse Location
on faceTheMouseLoc
--- Get the current location of the ship, this will be dynamic usually
put the loc of graphic "ship" into pCurrent

--- Get the current location of the mouse
put the mouseLoc into pTarget

--- Calculate the angle in degrees from the current coordinate to the target coordinate ie, source to target
put calculateAngle(item 1 of pCurrent, item 2 of pCurrent, item 1 of pTarget, item 2 of pTarget) into tAngle

--- Reset the angle of the 'ship' to it's original shape
--- Without the reset progressive deformation occurs

put "0,0" & comma & "-60,30" & comma & "-60,-30" into tPoints

--- Rotate the triangle polygon so that it is facing the new angle
put rotateTrianglePolygon(tPoints,tAngle) into tNewPoints

--- Add the first point of the polygon at the end of it's points so that it closes the graphic
put cr & line 1 of tNewPoints after tNewPoints

--- Set the graphic to the new rotated points
set the points of graphic "ship" to tNewPoints

--- Reset the position of the graphic because the modifications will redefine the origin
set the loc of graphic "ship" to pCurrent
end faceTheMouseLoc
xAction
Posts: 285
Joined: Thu Sep 16, 2021 1:40 pm
Contact:

xTalk Graphic Action Games Step By Step : Globals & Bullets

Post by xAction »

Global allow us to reuse variables with the same values we gave them in other functions and handlers.
We add them to the top of the script that needs them,

The first global we are going to use is
global gBulletsGenerated
--- total number of bullets is used for name of new bullet

This initBullet handler will be called every time a new bullet is generated, so to name them we track how many we have fired. We name them so we can later destroy them, don't get too attached to your bullets.
--- Define and create the graphic 'bullet<n>'
on initBullet
--- Set the name of the bullet
if gBulletsGenerated is empty then put 0 into gBulletsGenerated
add 1 to gBulletsGenerated
put "bullet" & gBulletsGenerated into tBulletName
set the name of the templateGraphic to tBulletName

set the style of the templateGraphic to oval
set the width of the templateGraphic to 5
set the height of the templateGraphic to 5

--- Set origin of the bullet to the ship
set the loc of the templateGraphic to loc of graphic "ship"

--- Pass the location of the target at the time the bullet is created to the bullet object
set the targetLoc of the templateGraphic to the mouseloc

--- Extract the bullet's script from the stack script
put the script of this stack into tScript

--- Add the global 'shotStep' at the top of the bullet script to track the bullet's progress
put "global shotStep" & cr into BULLET_SCRIPT

--- we need to work around the handler open/close commands
--- or the following attempt to get the handler via lineOffset will be the only lines we get
put "o" & "n" into tHandlerOpen
put "e" & "n" & "d" into tHandlerClose

--- define the handlers we want at this time
put "initShotStep,bulletTravel" into tBulletHandlers

--- loop through the desired handlers
repeat for each item tHandlerLabel in tBulletHandlers

--- Identify the first and last lines of the handler within the stack script
put lineOffset(tHandlerOpen && "BULLET_" & tHandlerLabel,tScript) into tStart
put lineOffset(tHandlerClose && "BULLET_" & tHandlerLabel,tScript) into tEnd

--- build the script from the results
put line tStart to tEnd of tScript & cr after BULLET_SCRIPT
end repeat

--- Removing the prefix will allow the bullet object to call the truncated form
replace "BULLET_" with empty in BULLET_SCRIPT

--- Copy the BULLET_SCRIPT into the next graphic created
set the script of the templateGraphic to BULLET_SCRIPT

--- Finally generate the bullet graphic
create graphic

--- Activate the bullet object's script
send InitShotStep to graphic tBulletName
end initBullet
User avatar
richmond62
Posts: 3270
Joined: Sun Sep 12, 2021 11:03 am
Location: Bulgaria
Contact:

Re: xTalk Graphic Action Games Step By Step

Post by richmond62 »

This is super.

BUT, for the 9-11 crowd I would keep away from sines & cosines, as in Bulgaria at least, these are not introduced to children until they are about 13.
https://richmondmathewson.owlstown.net/
xAction
Posts: 285
Joined: Thu Sep 16, 2021 1:40 pm
Contact:

xTalk Graphic Action Games Step By Step : Custom Properties & BULLET_SCRIPT

Post by xAction »

Rotate_Triangle_Shoot_At_Mouse.oxtStack
(12.27 KiB) Downloaded 67 times
RotateTriangle_shoot_At_Mouse.gif
RotateTriangle_shoot_At_Mouse.gif (25.92 KiB) Viewed 918 times
[Edit]- didn't notice that 'blink' out of existence until I recorded gif. Something wonky with my math somewhere. There's probably only so many decimal places that can be accurately rendered with integers at that size...so the solution will probably be to blow the triangle up to huge integers, rotate, and shrink all before the user can see it. That should work, maybe.

Here we introduce the concept of the custom property. These are arrays of data that can be held by objects. Objects are arrays with properties.

Code: Select all

set the width of field "example" to 12
set the exampleText of field "example" to "See what I mean?"
Here the script will store the mouse location into the new bullet graphic and the bullet will use that location to determine it's direction. A global could have been used, but I am also setting this location in the bullet definition script of the stack, and needed the init anyway. Having a custom property set in an object also allows individual objects to be selected and inspected to see what kind of script behavior is occurring.

When the bullet is fired it is completely new and needs its brand new custom property initialized.
Remember these scripts will be copied into the bullet object when it is created, the ugly "BULLET_" part will be removed, for consistency when we want something like "ENEMY_mouseUP" to respond as a normal mouseUp.

We store the target location once because if we update it, we get seeking missile effects. That's where scripts in objects come in handy, they can all have their own direction updates, vectors, effects , etc, without interfering with any other object
on BULLET_initShotStep[
--- memorize the targetLocation as a customProperty
set the targetLoc of me to the mouseLoc
--- proceed to travel
BULLET_bulletTravel
end BULLET_initShotStep

Now the actual traveling part of the bullet
on BULLET_bulletTravel
--- Initialization:
put item 1 the loc of graphic "ship" into shipX
put item 2 the loc of graphic "ship" into shipY

put item 1 the loc of me into bulletX
put item 2 the loc of me into bulletY

put item 1 of the targetLoc of me into targetX
put item 2 of the targetLoc of me into targetY

put 1 into bulletSpeed -- Adjust as needed

--- Calculate direction vector:
put targetX - shipX into deltaX
put targetY - shipY into deltaY
put sqrt(deltaX^2 + deltaY^2) into magnitude
put deltaX / magnitude into normalizedX
put deltaY / magnitude into normalizedY

--- Move bullet each cycle:
put bulletX + normalizedX * bulletSpeed into bulletX
put bulletY + normalizedY * bulletSpeed into bulletY

--- Update bullet position in your game
set the loc of me to bulletX,bulletY


--- refresh position with this script until the limit is reached
if the loc of me is within the rect of this card then
send bulletTravel to me in 10 milliseconds
else
--- when penetrating the screen edge, delete the bullet
--- BUT not from within it's own script, get help from the stack script
send DeleteGraphic && (the short name of me) to this stack in 100 milliseconds
end if
end BULLET_bulletTravel

Before we are done we have to be sure these bullets don't live forever,
on deleteGraphic tGraphicName
--- check for the existance of the graphic in case of piled up messages
if there is a graphic tGraphicName then delete graphic tGraphicName
end deleteGraphic
Now we'll add mouseDown and mouseStillDown handlers to shoot.
on mouseDown
--- Exit if we are using the scrollbar control or it will get messy
if the mouseloc is within the rect of scrollbar "angle" then exit mouseDown

--- Exit if the mouse is on the ship or there'll be no vectors for the bullet
if the mouseloc is within the rect of graphic "ship" then exit mouseDown

--- Check the mouse button to shoot
if the mouse is down then send mouseStillDown to me
end mouseDown
--- we are holding the mouse down, spam rapid fire!
on mouseStillDown
if the mouse is down then
--- something about mouseDown blocks other messages so keep tracking the mouse here
FaceTheMouseLoc
--- shoot!
initBullet
--- less time between updates = more bullet hell
send mouseStillDown to me in 200 milliseconds
end if
end mouseStillDown
I think I covered everything there. Have fun shootin'!
xAction
Posts: 285
Joined: Thu Sep 16, 2021 1:40 pm
Contact:

xTalk Graphic Action Games Step By Step: Summary One

Post by xAction »

A quick summary of what has been covered:
  • Scripting a Scrollbar graphical interface object to provide user interaction with a simulation
  • Create graphic objects with only x,y coordinates via script
  • Rotate x,y coordinates to redraw objects at varying angles
  • Tracking the mouse in the current window
  • Stopping program reactivity to the mouse under various conditions
  • Calculating the direction from one location to another location ('ship' to the mouse)
  • Writing handlers or functions within your main script that are intended to be copied into another object.
  • Creating new objects with their own scripts that allow them to run independent from our main script
  • Deleting objects (that are running scripts) by calling a handler in another object manages the deletion
  • Adding global variables to scripts to preserve data between functions and handlers of a script
  • Adding custom properties to objects which preserve data independently from any script
We have rather far to go and it's nose deep with code and comments.

I haven't actually defined what action game we are making. Here's the road map to action in my head at the moment.
  • A triangle 'ship'
  • Ship rotates to mouse
  • Ship shoots with mouse
  • Ship moves with impulse power during rawKeyDown events
  • Game Loop, ship persists to move after thrust in zero gravity
  • Ship obeys boundaries & constraints (pong vs. asteroids for example)
  • Everything is a ship and ship is everything let's call them GameObject
  • GameObject opponent/obstacle generation
  • GameObject collisions
  • GameObject goals, patterns, behaviors
  • GameObject react to each other within a given range ("It sees, it shoots!")
  • GameObject properties like health, type, speed, path
Spinning shooting ship shoots at spinning shooting ships. Its a tale as old as space.

Oh I noticed I used the global shotStep in the previous scripts. Here's an important lesson in deprecation, or deleting what you worked hard at figuring out because you figured out another way and the old way just confuses you as much as the next person to read the code. So shotStep was an attempt to destroy the bullets at a certain interval, trouble shooting another shooting issue I chose to rely on the ever dependable edges of the current card to decide the fate of the bullets. But in my rush to publish I left in some junked code and I wanted to give a heads up that it happens.
xAction
Posts: 285
Joined: Thu Sep 16, 2021 1:40 pm
Contact:

xTalk Graphic Action Games Step By Step : Dangers In Scripting

Post by xAction »

I wanted to alert you to the danger of some types of scripting. In the previous example I provide this code inside of the stack script, it is intended for the bullet object:
on BULLET_bulletTravel
//some code
put item 1 the loc of me into bulletX
put item 2 the loc of me into bulletY

put item 1 of the targetLoc of me into targetX
put item 2 of the targetLoc of me into targetY

// other code

--- Update bullet position in your game
set the loc of me to bulletX,bulletY

//etc
The me keyword is great, however if the stack runs the script from within it's stack script, for instance if you are testing handlers in the message box, you may find your window zooming off screen. Just be warned that 'me' in the stack script can cause unexpected behavior.

The handler says "BULLET_bulletTravel," who would expect it to move the stack?
User avatar
OpenXTalkPaul
Posts: 1871
Joined: Sat Sep 11, 2021 4:19 pm
Contact:

Re: xTalk Graphic Action Games Step By Step

Post by OpenXTalkPaul »

I don't want to clutter your thread with a lot of comments, I just wanted to say thank you posting it!
User avatar
richmond62
Posts: 3270
Joined: Sun Sep 12, 2021 11:03 am
Location: Bulgaria
Contact:

Re: xTalk Graphic Action Games Step By Step

Post by richmond62 »

Thank you.

HOWEVER, in light of my previous posting I will be posting my own thread with a much more 'babyish' sort of thing. 8-)
https://richmondmathewson.owlstown.net/
xAction
Posts: 285
Joined: Thu Sep 16, 2021 1:40 pm
Contact:

xTalk Graphic Action Games Step By Step : Keyboard Input

Post by xAction »

richmond62 wrote: Tue Feb 13, 2024 8:41 am Thank you.

HOWEVER, in light of my previous posting I will be posting my own thread with a much more 'babyish' sort of thing. 8-)
Necessary and appreciated endeavor, we are but few typing monkeys. Which brings us to the next part of the journey:
Keyboard Input via the built-in rawKeyDown handler

The "label field' at the top of previous stacks has been renamed to "StatusField" and has been given a custom property : defaultText like so :
set the defaultText of field "StatusField" to "Your text here."
You'll see when we press the keyboard that our next script displays the value of the key press at the end of the status field like this:
put the defaultText of field "StatusField" & tab & tab & tRawKey into field "StatusField"
You might use the same kind of script for some updating on screen game information like health, ammo count, lives remaining.


We will trap the W , S and Escape keys to nudge the ship towards or away from the mouse pointer.

Note that I escape the handler if we are in 'developer mode' , ie, not using the 'browse tool.'
The more we want screen or input updates the more we need to escape them when we don't want them, because some errors might trap us out of the IDE before we've saved our work.

Seeing these kind of escape the program commands should give you a clue that :
  • The function or handler in question (probably) gets called many times per second indefinitely
  • Not exiting while developing could be problematic.
on rawKeyDown tRawKey
--- do not capture the keyboard if we are in 'developer mode'
if the tool is not "browse tool" then exit rawKeyDown

--- View what the key values are while developing
put the defaultText of field "StatusField" & tab & tab & tRawKey into field "StatusField"

switch tRawKey
case 115 --- "s" key
send commandShip && "reverse" to graphic "ship"
break
case 119 --- "w" key
send commandShip && "forward" to graphic "ship"
break
case 65307 --- escape key
send stopShipw to graphic "ship"
break
end switch
end rawKeyDown
For the next script we will be cloning and modifying our BULLET_ code, which calculates the direction vector from an origin vector to a target vector and then travels in that direction forever or until destroyed. The SHIP_ scripts will move the graphic object toward or away from the mouse pointer.

Too be continued...
xAction
Posts: 285
Joined: Thu Sep 16, 2021 1:40 pm
Contact:

xTalk Graphic Action Games Step By Step : Chase The Mouse

Post by xAction »

Rotate_Triangle_Chase_Mouse_v4.oxtStack
(17.73 KiB) Downloaded 77 times
RotateTriangle_Chase_Mouse.gif
RotateTriangle_Chase_Mouse.gif (32.62 KiB) Viewed 917 times

First we'll add a global to the top of our object script, this allows us to set or change the speed out of the loop that uses it.
global shipSpeed
The response to our keyboard input will change the speed to forward, stop, or reverse. We use a switch case pattern to manage events or inputs to the same function or handler.

--- change shipSpeed and signal to update location or not
on PLAYER_shipCommand tCommand
--- initialize the global if it's empty doing math to empty is an error
if shipSpeed is empty then put 0 into shipSpeed
switch tCommand

case "forward"
put 2 into shipSpeed
shipUpdate
break

case "reverse"
put -2 into shipSpeed
shipUpdate
break

case "stop"
put 0 into shipSpeed
break
end switch
end PLAYER_shipCommand
Updates to the ships position will be called by a gate keeper handler shipUpdate
on shipUpdate
--- exit the update if the ship isn't moving or the pointer is outside of the window
if shipSpeed <> 0 and the mouseloc is within the rect of this card then
--- proceed to updating the location
shipTravel
--- update again after a playable delay
send updateShip to me in 250 milliseconds
end if
end shipUpdate
Now we modify our old BULLET_ script to work for a ship that persists and changes direction to chase the mouse.
on shipTravel
if the tool is not "browse tool" then exit shipTravel
--- Initialization:
--- The origin will be the center of the window
--- as our ship will be traveling to and from some vector toward or away from the center at all times

put width of this stack /2 into originX
put height of this stack /2 into originY

put item 1 the loc of me into currentX
put item 2 the loc of me into currentY

put item 1 of the mouseloc into targetX
put item 2 of the mouseloc into targetY

--- Ship shipSpeed can be less than or greater than one, but divide by 0 is the devil
--- Global shipSpeed should already be defined and initialized at 0
--- but if it's not then we'll initialize here and exit until the next call to shipTravel

if shipSpeed is empty then
put 0 into shipSpeed
exit shipTravel
end if

--- Calculate direction delta:
put targetX - originX into deltaX
put targetY - originX into deltaY


--- Avoid divide by zero error
if deltaX = 0 or deltaY = 0 or shipSpeed = 0 then exit shipTravel

--- calculate the movement modification
put sqrt(deltaX^2 + deltaY^2) into magnitude
put deltaX / magnitude into normalizedX
put deltaY / magnitude into normalizedY

--- Move bullet each cycle:
put currentX + normalizedX * shipSpeed into shipX
put currentY + normalizedY * shipSpeed into shipY

--- Ship beyond boundaries teleports to opposite side of screen,
if shipX < 10 then put Width of this stack-20 into shipX
if shipX > Width of this stack then put 20 into shipX
if shipY < 10 then put Height of this stack-20 into shipY
if shipY > Height of this stack then put 20 into shipY

--- Locate to the new position after adjustments
set the loc of me to shipX,shipY
end shipTravel

You'll find these handlers in the stack script as PLAYER_shipCommand, PLAYER_shipTravel, PLAYER_shipTravel"

But we'll actually be programmatically moving them into the player 'ship' when it is init, in the next script.
xAction
Posts: 285
Joined: Thu Sep 16, 2021 1:40 pm
Contact:

xTalk Graphic Action Games Step By Step: Init The Player's Ship

Post by xAction »

We now create the PLAYER 'ship' with built in scripts via the initPlayer handler. Now that we are now doing extra work to put scripts into the object it's important to identify it as more than just the triangle we generated earlier. PLAYER is a tricky word to use as the xTalk has a 'player' object of it's own for multimedia purposes. The need to point out that this is something a human person playing a game controls is important though. "SHIP_" could belong to a computer or player. Hopefully using PLAYER_ doesn't cause issues.

You'll see here we find the PLAYER_shipCommand ,etc scripts in the main stack script and then transfer them into the object we create for the player to manipulate.
on initPlayer tName
--- define the graphic for the object the player will control
set the style of the templateGraphic to "polygon"
set the points of the templategraphic to "0,0" &cr& "-12,6" &cr& "-12,-6" &cr& "0,0"
set the lineSize of the templateGraphic to 1
set the name of the templateGraphic to tName
set the visible of the templateGraphic to true
set the opaque of the templateGraphic to false
set the loc of the templateGraphic to width of this stack/2 , height of this stack /2

--- Extract the player's ship script from the stack script
put the script of this stack into tScript

--- Add the global 'shotStep' at the top of the bullet script to track the bullet's progress
put "global shipSpeed" & cr into PLAYER_SCRIPT

--- we need to work around the handler open/close commands
--- or the follwing attempt to get the handler via lineOffset will be the only lines we get
put "o" & "n" into tHandlerOpen
put "e" & "n" & "d" into tHandlerClose

--- define the handlers we want at this time
put "shipUpdate,commandShip,shipTravel" into tShipHandlers

--- loop through the desired handlers
repeat for each item tHandlerLabel in tShipHandlers

--- Identify the first and last lines of the handler within the stack script
put lineOffset(tHandlerOpen && "PLAYER_" & tHandlerLabel,tScript) into tStart
put lineOffset(tHandlerClose && "PLAYER_" & tHandlerLabel,tScript) into tEnd

--- build the script from the results
put line tStart to tEnd of tScript & cr after PLAYER_SCRIPT
end repeat

--- Removing the prefix will allow the player ship object to call the truncated handler form
replace "PLAYER_" with empty in PLAYER_SCRIPT

--- Copy the PLAYER_script into the new graphic we intend to create
set the script of the templateGraphic to PLAYER_SCRIPT

--- clones are bad for business, terminate on sight.
if there is a graphic tName then delete graphic tName

--- Finally generate the player ship graphic
create graphic tName

end initPlayer
At this point besides our call to field "StatusField" this script will do exactly what want it to do no matter what stack or card script we copy and paste it into, and our ship is fun to interact with.
xAction
Posts: 285
Joined: Thu Sep 16, 2021 1:40 pm
Contact:

xTalk Graphic Action Games Step By Step : Game Loop Basics

Post by xAction »

Many changes to this stack.
xTalk_Action_Game_Chase_Mouse_Game_Loop.gif
xTalk_Action_Game_Chase_Mouse_Game_Loop.gif (54.38 KiB) Viewed 916 times



First I've begun to compartmentalize the script commands, here's some pseudo code to illustrate.

  • init.Something initializes global values and/or signals required UI element creation
  • UI.createSomething generates a UI object
  • Math.CalculationExpectation manipulates mathematical properties
  • Script.CodeAnything generates other script commands or manipulates the script

    This isn't psuedo code:
  • Game.loop runs only if Game.validatePlaying allows it
  • Game.Loop signals updates via Turns ie,Turn.player and Turn.bullet
Scripts nested in the stack script that get copied to other object still begin with all caps of their destination type like:
PLAYER_SHIP_shipCommand and BULLET_initBullet

Hopefully it will easier to follow what does what.

Now using lock screen / unlock screen in the Game.Loop

The scroll bar has been removed and UI.createScrollbarAngle is available in the stack script to illustrate what a bunch of work programming would be if it wasn't drag and drop. yikes. I'm going to remove that in future stacks.

There is now a card script on card 1 :
on openCard
init.App
end openCard
When the program window opens all essential elements of the program will be initialized:
on init.App
init.Globals
init.UI
init.Game
put true into bAppInit
end init.App
There's a cascade of explanations to come, but I want to get the stack up now because it's fun at what it does.
xAction
Posts: 285
Joined: Thu Sep 16, 2021 1:40 pm
Contact:

xTalk Graphic Action Games Step By Step : Game Loop Init Globals

Post by xAction »

A number of new globals have been added to the stack script and the objects
I like commenting what global variables do in the definitions part of the script
global bAppInit --- nothing else runs until app completes init sequence
global bPlaying --- player has no control while bPlaying = false
global gBulletsGenerated --- counts total bullets fired for naming new bullets
global playerShipSpeed --- speed of the the object the player controls
global gDefaultShipSpeed --- don't allow ramping from rapid key presses
globla gLastKeyDown --- what speed should a ship be?
but I tried something different this time by placing that in the initialization
on init.Globals
put false into bAppInit --- nothing else runs until app completes init sequence
put false into bPlaying --- player has no control while bPlaying = false
put 0 into gBulletsGenerated --- counts total bullets fired for naming new bullets
put 1 into playerShipSpeed --- speed of the the object the player controls
put "" into gLastKeyDown --- don't allow ramping from rapid key presses
put 1 into gDefaultShipSpeed --- what speed should a ship be?
end init.Globals
I'll probably stick to the initialization for now. The default value itself might need a note.

After the globals are init the display is enabled, or when you delete everything then everything will be respawned at runtime.
on init.UI
if there is not a field "StatusField" then UI.createFieldStatusField
if there is not a graphic "Ship" then UI.createGraphicShip "Ship"
end init.UI
UI.createGraphicShip will now take the name of the object to create as an argument...so you know "Enemy" is right around the corner.

at the end of the current init.App
on init.Game
Game.validatePlaying
end init.Game

Game.validatePlaying prevents the game loop from continuing to run rampant if we switch to developer mode or leave the safe space of the stack rect. This is the only other place in the scripts that 'send in milliseconds is used.
The other is a delay for the bullet firing from the mouse down.
on Game.validatePlaying
--- ensure everything we need exists
if there is not a graphic "ship" then init.App
--- only react if we are not in developer mode or mousing around outside the stack
if the tool is not "browse tool" or the mouseloc is not within 0,0,width of this stack,height of this stack then
put false into bPlaying
exit Game.validatePlaying
else
--- allow the game loop to run
put true into bPlaying
send Game.loop to this stack in 150 milliseconds
end if
end Game.validatePlaying
Here it comes, the big Game.Loop everyone has been talking about!
on Game.loop
--- gatekeep if the game loop can run at all with bPlaying global
if bPlaying is false then exit Game.loop
--- lock out screen updates
lock screen
--- update the player 'ship' information , location etc
Turn.player
--- move every bullet
Turn.bullets
--- refresh all graphics
unlock screen
--- loop back after a playable delay if conditions allow
Game.validatePlaying
end Game.loop
But wait...there's more!
xAction
Posts: 285
Joined: Thu Sep 16, 2021 1:40 pm
Contact:

xTalk Graphic Action Games Step By Step: Game Loop Turns

Post by xAction »

Anything that gets updated during the game loop does so in Turn. currently that is simply, the player and their bullets.

on Turn.player
if bPlaying is true then
--- track the angle from mouse to the player ship
UI.FaceTheMouseLoc "ship"
--- if thrust has been engaged in either direction then fly
if playerShipSpeed <> 0 then send shipUpdate to graphic "ship"
end if
end Turn.player
UI.FaceTheMouseLoc now accepts a graphic object name for an argument, a harbinger of triangle shaped enemies!

Moving all the bullets at once really is this easy:
on Turn.bullets
--- count all the objects from high to low because we may have deleted an object
repeat with i = number of graphics of this stack down to 1
--- we may have deleted the graphic so check that its still there
if there is a graphic i of this stack then
--- get the name
put the short name of graphic i of this stack into tCurrentGraphic
--- is it a "bullet"?
if char 1 to 6 of tCurrentGraphic is "bullet" then
--- bullet in window continues to travel
if loc of graphic tCurrentGraphic is within 0,0,width of this stack,height of this stack then
send bulletTravel to graphic tCurrentGraphic
else
--- if bullet is outside of window then bullet is destroyed
UI.deleteGraphic tCurrentGraphic
end if
end if
end if
end repeat
end Turn.bullets
xAction
Posts: 285
Joined: Thu Sep 16, 2021 1:40 pm
Contact:

xTalk Graphic Action Games Step By Step : Game Loop Player Ship

Post by xAction »

I've made numerous changes to the player ship. Best to start at the init.
on UI.createGraphicShip tName
--- define the graphic for the object the player will control
set the style of the templateGraphic to "polygon"
set the points of the templategraphic to "0,0" &cr& "-12,6" &cr& "-12,-6" &cr& "0,0"
set the lineSize of the templateGraphic to 1
set the name of the templateGraphic to tName
set the visible of the templateGraphic to true
set the opaque of the templateGraphic to false
set the loc of the templateGraphic to width of this stack/2 , height of this stack /2

--- Extract the player's ship script from the stack script
put the script of this stack into tScript

--- Add the globals at the top of the script
put "global shipSpeed" & cr into PLAYER_SCRIPT
put "global gDefaultshipSpeed" & cr after PLAYER_SCRIPT
--- we need to work around the handler open/close commands
--- or the follwing attempt to get the handler via lineOffset will be the only lines we get
put "o" & "n" into tHandlerOpen
put "e" & "n" & "d" into tHandlerClose

--- define the handlers we want at this time
put "shipUpdate,shipCommand,shipTravel" into tShipHandlers

--- loop through the desired handlers
repeat for each item tHandlerLabel in tShipHandlers

--- Identify the first and last lines of the handler within the stack script
put lineOffset(tHandlerOpen && "PLAYER_SHIP_" & tHandlerLabel,tScript) into tStart
put lineOffset(tHandlerClose && "PLAYER_SHIP_" & tHandlerLabel,tScript) into tEnd

--- build the script from the results
put line tStart to tEnd of tScript & cr after PLAYER_SCRIPT
end repeat

--- Removing the prefix will allow the bullet object to call the truncated form
replace "PLAYER_SHIP_" with empty in PLAYER_SCRIPT

--- Copy the PLAYER_script into the new graphic created
set the script of the templateGraphic to PLAYER_SCRIPT

--- clones are bad for business, terminate on sight.
if there is a graphic tName then delete graphic tName

--- Finally generate the bullet graphic
create graphic tName
--- added this last bit while trouble shooting, not sure if I like it there
put true into bPlaying
end UI.createGraphicShip

The scripts that that the player will contain are identified by the "PLAYER_SHIP_" prefix in the stack script. I'll truncate those here. We'll start with a look at the globals that are added.
global shipSpeed
global gDefaultshipSpeed
shipSpeed is the current speed of the ship that we set with the keyboard
gDefaultshipSpeed is whatever value we think the ships should default to, which seems to be 1, but I was playing with values and chasing it down all over the place so one global to access appears to be the solution

shipUpdate is the gatekeeper for activity of the ship
on shipUpdate
-- refresh the direction of the ship
UI.FaceTheMouseLoc "ship"
--- initialize the speed values as this object may have spawned after other inits have fun
if shipSpeed is empty or gDefaultShipSpeed is empty then
put 1 into gDefaultShipSpeed
put gDefaultShipSpeed into shipSpeed
end if
--- exit the update if the ship isn't moving or our pointer is outside of the window
if shipSpeed <> 0 and shipSpeed <> empty and the mouseloc is within the rect of this card then
--- proceed to update the location
shipTravel
end if
end shipUpdate
Changes I made to shipTravel allow it to control objects from the stack script, as opposed to being installed in the objects. Not sure that was my intent. I think I was just preparing for objects to have targets other than the mouse. We'll see what happens soon.

It takes two arguments:
tTravelObjectName is the object that is being moved
tTargetXY is x,y Location coordinates to move toward
if these values are empty the script defaults to act on the object that contains the script

Note there's a lot of reasons to exit this script, and 99% of them are division by zero errors.
on shipTravel tTravelObjectName,tTargetXY
--- if not initialized then initalize and exit this loop
if gDefaultShipSpeed is empty then
put 1 into gDefaultShipSpeed
put gDefaultShipSpeed into shipSpeed
exit shipTravel
end if
if the tool is not "browse tool" then exit shipTravel
--- Initialization:
--- The origin will be the center of the window
--- as our ship will travel at some vector toward or away from it at all times
put width of this stack /2 into originX
put height of this stack /2 into originY
if tTravelObjectName is not empty then
put item 1 the loc of graphic tTravelObjectName into currentX
put item 2 the loc of graphic tTravelObjectName into currentY
else
put item 1 the loc of me into currentX
put item 2 the loc of me into currentY
end if

--- target something other than the mouse position, who, or what matters not
if tTargetXY is not empty then
put item 1 of tTargetXY into targetX
put item 2 of tTargetXY into targetY
else
put item 1 of the mouseloc into targetX
put item 2 of the mouseloc into targetY
end if
--- Ship shipSpeed can be less than or greater than one, but divide by 0 is the devil
--- Global shipSpeed should already be defined and initialized at 0
--- but if it's not then we'll initialize here and exit until the next call to shipTravel
if shipSpeed is empty or shipSpeed is not a number then
put 0 into shipSpeed
exit shipTravel
end if

--- Calculate direction vector:
put targetX - originX into deltaX
put targetY - originY into deltaY

--- Avoid divide by zero error
if deltaX = 0 or deltaY = 0 or shipSpeed = 0 then exit shipTravel

put sqrt(deltaX^2 + deltaY^2) into magnitude
--- absolutely no divide by zero ever
if magnitude = 0 then exit shipTravel

put deltaX / magnitude into normalizedX
put deltaY / magnitude into normalizedY

--- Move bullet each cycle:
put currentX + normalizedX * shipSpeed into shipX
put currentY + normalizedY * shipSpeed into shipY

--- Ship beyond boundaries loops to opposite side of screen,
if shipX < 10 then put Width of this stack-20 into shipX
if shipX > Width of this stack then put 20 into shipX
if shipY < 10 then put Height of this stack-20 into shipY
if shipY > Height of this stack then put 20 into shipY

--- Locate to the new position after adjustments
if tTravelObjectName is not empty then
set the loc of graphic tTravelObjectName to shipX,shipY
else
set the loc of me to shipX,shipY
end if
end shipTravel
Last but not least respond to keyboard input sent from the stack script

Code: Select all

on PLAYER_SHIP_shipCommand  tCommand
   if shipSpeed is empty then put 0 into gDefaultShipSpeed
   switch tCommand
      case "forward" 
         put gDefaultShipSpeed into  shipSpeed
         shipUpdate
         break
      case "reverse"
         put gDefaultShipSpeed*-1 into shipSpeed
         shipUpdate
         break
      case "stop"
         put 0 into shipSpeed
         break
   end switch
end PLAYER_SHIP_shipCommand
xAction
Posts: 285
Joined: Thu Sep 16, 2021 1:40 pm
Contact:

Re: xTalk Graphic Action Games Step By Step

Post by xAction »

xTalk_Action_Game_Enemy_Combat.oxtStack
(100.7 KiB) Downloaded 76 times
xTalk_Action_Game_Enemy_Combat.gif
xTalk_Action_Game_Enemy_Combat.gif (108.05 KiB) Viewed 914 times
Wooo enemies! Beware the red triangles!

Had to make some changes, of course. Lots of things I thought would work didn't.

First I added a function to make a circle using points, this gets shrunken to the screen rect and the point data is used as a random location for the enemies to spawn within the window but not right on top of the player, generally.

When the enemies spawn they pick an entirely arbitrary random location to point at and travel to
on PLAYER_SHIP_ShipPickTarget
put centerX() + (random(1000) - random(1000)) , centery() + (random(1000)-random(1000)) into gTargetXY
end PLAYER_SHIP_ShipPickTarget
if their target is offscreen they will choose another target
if they hit the screen boundaries they change direction

They still seem to be targeting the mouse, that's odd. It works, but it's weird, I'll figure something else out.

Their current movement is a bit jerky. Whole game actually starts real slow and ramps up in speed, not sure how. Send in x milliseconds piling up somewhere? I removed that and it was crazy fast. I put in wait 1 tick and the bullet firing for the player broke.

If the enemy is within 100 units of the player they will shoot...sometimes, there's a random number gate that controls their itchy trigger fingers like:
if random(6) > random(100)
The function calculateDistance determines the proximity of the enemy to the player
function calculateDistance pX1, pY1, pX2, pY2
-- pX1, pY1: Coordinates of the first point
-- pX2, pY2: Coordinates of the second point
put (pX2 - pX1) ^ 2 + (pY2 - pY1) ^ 2 into distanceSquared
put sqrt(distanceSquared) into distance
return distance
end calculateDistance
Enemies sometimes vanish for no reason, not sure why, probably their speed.
They die from friendly fire.
Player doesn't die from enemy hits, hits just take 1000 from the score.

And that's all I can stay awake for, have fun!
Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests