Jump to content
IGNORED

Flip sprite on/off


Recommended Posts

My test game is coming along nicely, and it's time to animate some sprites, I was wondering about Flip X. Is it possible to have it tied to a variable so I could cheat and animate the sprite by simply setting the flip to on and off? Any tips/guides/samples would be great!

Link to comment
Share on other sites

I suppose you can set a variable that is either 0 or FLIP_X, e.g. flipx = FLIP_X - flipx, if flipx begins at 0. Then you need to update each sprite, adding this variable instead of a constant, just like you would add x and y position of the sprite.

Link to comment
Share on other sites

2 hours ago, LemonSkull69 said:

My test game is coming along nicely, and it's time to animate some sprites, I was wondering about Flip X. Is it possible to have it tied to a variable so I could cheat and animate the sprite by simply setting the flip to on and off? Any tips/guides/samples would be great!


You could “tie” the flip state to a variable, like @carlsson suggested.  That way, on every frame, you can update the sprite record according to its animation state.

 

Just remember that sprite changes are applied on every frame, so using the SPRITE statement is not enough — you must use the WAIT statement at least once per iteration in your game loop to update the actual sprite hardware.

 

Let me know if you have any questions or if anything above is not clear.

 

    dZ.

Link to comment
Share on other sites

Depending on what type of game it is, perhaps you already have a DX variable that keeps track of the direction the player is moving. Then you could use it in conjunction with the constant for FLIPX $0400 instead of having a specific variable, unless of course you want to flip one or more sprites based on a different condition than direction of movement.

 

I wonder if you can set up an ON FRAME routine that does certain things, like updating sprites once per frame so it would read your variable automatically. Like DZ-Jay mentioned, you need to WAIT for vertical blank, but perhaps the ON FRAME routine runs at or next to the blanking anyway?

Link to comment
Share on other sites

9 hours ago, carlsson said:

Depending on what type of game it is, perhaps you already have a DX variable that keeps track of the direction the player is moving. Then you could use it in conjunction with the constant for FLIPX $0400 instead of having a specific variable, unless of course you want to flip one or more sprites based on a different condition than direction of movement.

 

I wonder if you can set up an ON FRAME routine that does certain things, like updating sprites once per frame so it would read your variable automatically. Like DZ-Jay mentioned, you need to WAIT for vertical blank, but perhaps the ON FRAME routine runs at or next to the blanking anyway?


The ON FRAME routine is called within the context of the ISR (Interrupt Service Routine), but after the STIC, GRAM, and PSG are updated.  So even if you were to access sprite registers directly, it may be too late, past the vertical blanking (VBLANK) period.

 

Anyway, it is not clear to me from the original question was is meant by "tie to a variable" and "animate the sprite."

 

I can think of two possible meanings:

  • Flip the sprite to face the direction of horizontal travel.  This can be easily accomplished, and I do it in my own games:  my sprite pictures are always oriented to the right by default, so when they are going left, I set the FLIP flag.  I simply use a variable to track the direction of travel, and test it with an IF statement, then use a combination of AND and XOR operations to clear or set the flag, as appropriate.  I do that test whenever the horizontal direction changes.

 

  • Make the sprite appear to "shake" by continually flipping its horizontal orientation.  This is even easier since there is no direction to track nor condition to test.  I also have used this technique in my own games:  on every key frame of animation during the shaking sequence, I just toggle the FLIP flag of the sprite with an XOR operation.

 

I hope the above helps, but it is difficult to provide appropriate assistance without more details.  Let us know what specifically you are looking to do, and we may even be able to offer sample code and additional help.

 

    dZ.

  • Like 1
Link to comment
Share on other sites

I'm sorry if I wasn't clear. I will try to clarify: My game is is about shooting zombies, the player is at the bottom of the screen moving left and right. The zombie moves once every second down the screen 8 pixels at a time. Upon reaching the bottom the game chooses a new x position for the zombie and moves it back to the top of the screen at the new x position, rince and repeat.

 

What I meant by "Tie to a variable" was I wasn't sure if Intellivision would allow "Definitions"? like VISIBLE, ZOOMX2, FLIP_X, etc to be used by anything else then SPRITE and pondered such things might have been contained only to SPRITE. But now I know these things can be influenced from elsewhere in the code.

 

What I meant by "Animate the sprite" is having the zombie sprite flip if it's not already flipped when the timer tells the zombie it's time to move down, and unflip if it's flipped. Here's the code to make the zombie move down:

 rem TIC=Timer, Z1Y=Zombie 1's Y coordinate.
IF TIC=60 THEN Z1Y=Z1Y+10:TIC=0

 

I'm working on the information you gave me, it's very helpful, though it takes time as I am a beginner 😃

Link to comment
Share on other sites

3 hours ago, LemonSkull69 said:

I'm sorry if I wasn't clear. I will try to clarify: My game is is about shooting zombies, the player is at the bottom of the screen moving left and right. The zombie moves once every second down the screen 8 pixels at a time. Upon reaching the bottom the game chooses a new x position for the zombie and moves it back to the top of the screen at the new x position, rince and repeat.

 

Sounds interesting.

 

3 hours ago, LemonSkull69 said:

What I meant by "Tie to a variable" was I wasn't sure if Intellivision would allow "Definitions"? like VISIBLE, ZOOMX2, FLIP_X, etc to be used by anything else then SPRITE and pondered such things might have been contained only to SPRITE. But now I know these things can be influenced from elsewhere in the code.

 

Those are IntyBASIC constants.  They are just numeric values with a symbolic name.  They can be used in any place where you would use a number.  In practice, the compiler will replace any constants with their corresponding numeric values when it evaluates an expression.

 

IntyBASIC supports two types of symbolic values:

  • Constants:  These are symbols that are assigned a constant numeric value and can never change.  This is what we use to define things lke VISIBLE, ZOOMX2, FLIP_X, etc.  You use the "Const" keyword to define constants.  Take a look at the "constants.bas" module file that comes with the IntyBASIC SDK (or in the "contrib" folder that comes with the IntyBASIC compiler) for the set of useful constants we typically use in an IntyBASIC program.  That file was created and maintain by various individuals in this community.  You, of course, can create your own constants.  Constants are always 16-bit values.

    Below are some examples from the "constants.bas" file.  Notice that the value must be a number or an arithmetic expression containing only numbers.  It cannot be another constant nor any expression containing anything else than numeric values.
Spoiler
' -------------------------------------------------------------------------
' Note: Add these constants to the sprite command's X parameter.
' -------------------------------------------------------------------------
CONST HIT                               = $0100         ' Enable the sprite's collision detection.
CONST HIDDEN                            = $0000         ' Make the sprite visible.
CONST VISIBLE                           = $0200         ' Make the sprite visible.
CONST ZOOMX1                            = $0000         ' Make the sprite default width (x1).
CONST ZOOMX2                            = $0400         ' Make the sprite twice the width.

' ...

' -------------------------------------------------------------------------
' PSG - Volume control.
' -------------------------------------------------------------------------
' Notes:
' - For use in the volume field of the SOUND command.
' - Internal channels: PSG_CHANNELA, PSG_CHANNELB, PSG_CHANNELC
' - ECS channels: PSG_ECS_CHANNELA, PSG_ECS_CHANNELB, PSG_ECS_CHANNELC
' -------------------------------------------------------------------------
CONST PSG_VOLUME_MAX                    = 15    ' Maximum channel volume.
CONST PSG_ENVELOPE_ENABLE               = 48    ' Channel volume is controlled by envelope generator.

' ...

' -------------------------------------------------------------------------
CONST CS_COLOUR_SQUARES_ENABLE          = $1000
CONST CS_PIX0_BLACK                     = 0
CONST CS_PIX0_BLUE                      = 1
CONST CS_PIX0_RED                       = 2
CONST CS_PIX0_TAN                       = 3
CONST CS_PIX0_DARKGREEN                 = 4
CONST CS_PIX0_GREEN                     = 5
CONST CS_PIX0_YELLOW                    = 6
CONST CS_PIX0_BACKGROUND                = 7
CONST CS_PIX1_BLACK                     = 0
CONST CS_PIX1_BLUE                      = 1*8
CONST CS_PIX1_RED                       = 2*8
CONST CS_PIX1_TAN                       = 3*8
CONST CS_PIX1_DARKGREEN                 = 4*8
CONST CS_PIX1_GREEN                     = 5*8
CONST CS_PIX1_YELLOW                    = 6*8
CONST CS_PIX1_BACKGROUND                = 7*8

 

 

 

  • Variables:  These are symbols representing memory addresses at which you can store values temporarily for use throughout your program.  There are two types of variables, corresponding to the two types of memory supported by the Intellivision:  8-bit (byte) variables and 16-bit (word) variables.  You use the "Dim" keyword to define both types of variables.  You can assign any value at all to them at any time.  In contrast to constants, which can only be assigned a single constant numeric value, variables can be assigned more complex formulas and function expressions.  However, the value must ultimately evaluate to a number, so no strings or characters are supported.

    Below are some examples from one of my programs.  Notice that You can assign the result of functions and other complex expressions to any variable.
Spoiler
    ' 8-bit variables
    Dim CurrPosX
    Dim CurrPosY
    Dim CurrPos
    Dim TempPos

    ' 16-bit variables
    Dim #DeltaX
    Dim #DeltaY


    ' ...

    CurrPosX = (Random(16) + 2)
    CurrPosY = (Random( 8) + 2)
    TempPos  = GetScreenPos(CurrPosX, CurrPosY)

    ' ...

    CurrPos  = TempPos

    ' ...

    #DeltaY = Abs( GetObjTargetDeltaY(CurrObject, CurrTarget) )
    #DeltaX = Abs( GetObjTargetDeltaX(CurrObject, CurrTarget) )

    If (#DeltaX < #DeltaY) Then
        #DeltaX = (#DeltaX Xor #DeltaY)
        #DeltaY = (#DeltaX Xor #DeltaY)
        #DeltaX = (#DeltaY Xor #DeltaX)
    End If

 

 

You may want to take a look at the IntyBASIC manual document that comes with the compiler for more information on the use of variables and the important differences between 8-bit and 16-bit variables.  Or just ask here if you have any specific questions.

 

3 hours ago, LemonSkull69 said:

What I meant by "Animate the sprite" is having the zombie sprite flip if it's not already flipped when the timer tells the zombie it's time to move down, and unflip if it's flipped. Here's the code to make the zombie move down:

 rem TIC=Timer, Z1Y=Zombie 1's Y coordinate.
IF TIC=60 THEN Z1Y=Z1Y+10:TIC=0

 

If all you want is to toggle the flip setting on and off, then you can easily accomplish that using an XOR operation.  XOR (eXclusive OR) will return 1 when two bits are different, and zero otherwise.  So:

  • 1 XOR 1 = 0
  • 0 XOR 0 = 0
  • 1 XOR 0 = 1
  • 0 XOR 1 = 1

The above behaviour is very useful for flipping bits.  To illustrate, take a look at what happens when you XOR two binary numbers:

' XOR with zero:

    10111011
XOR 00000000
------------
    10111011

' XOR with one:
    10111011
XOR 11111111
------------
    01000100

' XOR with a mask to flip specific bits:
    10111011
XOR 01001000
------------
    11110011

 

Notice that when you XOR with zero, you get the original value out:  that is because each "1" bit XOR'ed with zero will return "1" (1 XOR 0 = 1); and each "0" bit XOR'ed with zero will return "0" (0 XOR 0 = 0).

 

Also notice that when you XOR with one, you turn all the "1" bits to zero, and all the "0" bits to one, for precisely the same reasons.

 

Lastly, notice that we can take advantage of that behavior by specifying with a "1" specific bits we want to flip, and leaving all other bits as "0."  The result is that all the bits that are XOR'ed with "0" are left as is, and all the bits with "1" will be flipped. :)

 

So, if you want to toggle the FLIPX bit (bit 10, or value $0400) of a sprite, all you have to do is take the value of the sprite's Y register (the third argument to the SPRITE statement), and XOR it with the FLIPX value.

 

Let us know if you need help with this.

 

3 hours ago, LemonSkull69 said:

I'm working on the information you gave me, it's very helpful, though it takes time as I am a beginner 😃

 

No worries, I understand.  I was a beginner starting from scratch too not so long ago. :)

 

     -dZ.

 

Link to comment
Share on other sites

No worries.   Keep us abreast of your progress -- I would like to see how it develops. :)

 

Also, if you want help with your code, or suggestions for improvements, you can always post it here.

 

   dZ.

Link to comment
Share on other sites

I do have a question about updating printed variables. The idea is having a 6 figure score counter. And upon going to the gosub score the number in #score goes up by 10. I can't figure out how to achieve this. I imagined it would be something like the code below, but the number does not change. Any words of wisdom?

Bare minimum of relevant pieces of code:
#score=1000
	PRINT AT SCREENPOS(0, 0) COLOR CS_YELLOW,"Score: ",<6>#score
loop:
WAIT
		IF COL1 AND HIT_SPRITE2 THEN GOSUB score
GOTO loop
score: PROCEDURE	
	 Z1Y=10:ZX=RAND(190):CANSHOOT=0:MY=0:MX=0
	#score=#score+10
	RETURN
	END

 

Link to comment
Share on other sites

I am sure that #score increases, just that your score display is executed outside of the main loop so it is only printed once. The other way to do it would be to separate the score display into a subroutine of its own, which you GOSUB before the loop and again GOSUB within the "score" procedure.

 

Nothing or very few things update automagically in IntyBASIC. You can have one routine that is called on ISR as DZ-Jay described, but you probably don't want to cram it with all sorts of graphic and sound updates, calculations etc. In the case of changing a sprite, updating a text etc it often is better to do that in conjunction to changing the value to be updated, perhaps add a WAIT to ensure the execution doesn't continue until it has triggered.

  • Like 1
Link to comment
Share on other sites

By the way, RAND() updates once per frame. If you need multiple random numbers in a sequence, use RANDOM() which triggers a new random number. Both are marked as "slow" in the manual, but I never compared the generated code to see how much slower the second one is.

 

/signed someone who sprouts a lot of random numbers in his current project

  • Like 1
Link to comment
Share on other sites

5 hours ago, LemonSkull69 said:

I do have a question about updating printed variables. The idea is having a 6 figure score counter. And upon going to the gosub score the number in #score goes up by 10. I can't figure out how to achieve this. I imagined it would be something like the code below, but the number does not change. Any words of wisdom?

Bare minimum of relevant pieces of code:
#score=1000
	PRINT AT SCREENPOS(0, 0) COLOR CS_YELLOW,"Score: ",<6>#score
loop:
WAIT
		IF COL1 AND HIT_SPRITE2 THEN GOSUB score
GOTO loop
score: PROCEDURE	
	 Z1Y=10:ZX=RAND(190):CANSHOOT=0:MY=0:MX=0
	#score=#score+10
	RETURN
	END

 

 

@carlsson already provided the correct answer, but below is some code on the way I would do that:

 


' ...

#score = 1000

Loop:
    Wait

    IF (COL1 AND HIT_SPRITE2) THEN
        ' Sprite is hit -- handle it.
        Gosub HandleHit
    End If

    ' Update score display
    Print At SCREENPOS(0, 0) COLOR CS_YELLOW, "Score: ", <6>#score

Goto Loop


HandleHit: Procedure
    ' Update sprite state
    ZX  = RAND(190)
    MY  = 0
    MX  = 0
    Z1Y = 10
    CANSHOOT = 0

    ' Increment player score
    #score = (#score + 10)

    Return
End

 

You can safely ignore the above code structure and layout if you'd like -- I am sort of a stickler for clean and easy to follow code, though.  In any case, the important thing to note is that the score is updated by the HandleHit() procedure (I personally wouldn't call it "score" since it does much more than update the score, but that's me) and the score display is then updated at the bottom of the loop.

 

     -dZ.

  • Like 1
Link to comment
Share on other sites

That is a good suggestion, in case more than one action can affect the score. It means the display isn't updated until all actions have been carried out for each turn in the main loop. For an action game, that probably is the right way to go, but for a strategy game where multiple actions may take time to play, you might want to update the status display sooner than after all actions are done.

Edited by carlsson
  • Like 1
Link to comment
Share on other sites

4 hours ago, carlsson said:

That is a good suggestion, in case more than one action can affect the score. It means the display isn't updated until all actions have been carried out for each turn in the main loop. For an action game, that probably is the right way to go, but for a strategy game where multiple actions may take time to play, you might want to update the status display sooner than after all actions are done.

 

That is true, but updating the score is expensive:  it requires the number to be divided by 10 multiple times, and then the output needs to be printed out in a loop.  Personally, I rather update the score once per frame, or maybe even once every other frame.  Even if you update the score display once every 30 frames, that's still twice per second, which is rather quick to the human eye.

 

     -dZ.

Link to comment
Share on other sites

That is very helpful!

 

I have another 2 questions:

The project has grown pretty large and I'm starting having trouble thinking about juggling multiple variables interacting with each other, to me it's getting rather complicated. Do you have any tips on that, or is it just a matter of getting used to it with time?

 

The second question: Switching out/Replacing sprite displayed; At the moment when the zombies collide with the bullet, they dissapear and gets reused, I'm trying to figure out how to replace the zombie sprite with a death animation before that. Should I use the same principle as I did with the flipx or is there other ways to do it?

Link to comment
Share on other sites

2 hours ago, LemonSkull69 said:

That is very helpful!

 

I have another 2 questions:

The project has grown pretty large and I'm starting having trouble thinking about juggling multiple variables interacting with each other, to me it's getting rather complicated. Do you have any tips on that, or is it just a matter of getting used to it with time?


Here are my personal tips for variables:

  • Use OPTION EXPLICIT and declare all your variables at the top, or in a separate file imported at the top of your program.
  • Use sensible names for variables that describe what values they hold and for what purpose.  I prefer things like “EnemyPosX” and “#PlayerScore,” rather than “X” and “S.”
  • Dedicate variables to specific values you need to track — irrespective of how many times you need to access those values.  There is plenty of memory for variables in most cases, and you should only try to consolidate or re-use variables when absolutely necessary and you know exactly what you are doing and how.
  • Avoid creating generic “temp” variables unless they are localized to a specific routine or you can guarantee that they will not collide across uses.
  • When it becomes necessary to have one-off temporary variables for interim calculations within procedures, define special purpose variables for them.  (For example, one of my subroutines computes the distance between two sprites and needs temporary variables for its processing, so I declared “#TempDeltaX,” “#TempDeltaY,” and “TempAngle.”  It does not bother me one whit that these variables are used only in this procedure and nowhere else.)
  • Group your variable declarations by category, by use, or by function, etc.  For example, place all declarations for variables related to the player sprite together; all the enemy-related variables together; all bullet-related variables together, etc.
  • Name related variables with a common prefix (e.g., “PlayerPosX,” “PlayerPosY,” “PlayerStatus,” “PlayerState,” “PlayerDirection,” etc.).
  • Be consistent in your naming conventions.  For instance, if you have a variable called “PlayerPosX,” you may want to name the horizontal position of your enemy “EnemyPosX.”  Avoid inconsistencies like “posX” in one case and “Xpos” in another.
  • Avoid naming variables with similar labels that may cause any confusion.  For instance, instead of “posX1” for the player position and “posX2” for the enemy position, consider more explicit names, such as “PlayerPosX” and “EnemyPosX.”  (A good rule of thumb is to avoid variable names that differ only by a single character.)
  • Declare special variables for use exclusively as input or output arguments to procedures.  For example, if you have a procedure that takes a sprite number and a direction value and updates the sprite velocity to move in the new direction, you may want to declare two variables, such as “InputSprite” and “InputDir” and only use them for that specific purpose.
  • Document use of variables in procedures with a short header comment.  You may want to list the variables it reads to do its work, the variables it updates, and any other temporary variables it uses for scratch.  This will allow you to track what parts of your code affect which variables.


If I had to boil down the above into a single general recommendation, it would be this:  Be clear and consistent.

 

It does not matter what names you use or how you define your variables, just as long as they make sense to you, and you apply your naming conventions consistently to avoid confusion.

 

2 hours ago, LemonSkull69 said:

The second question: Switching out/Replacing sprite displayed; At the moment when the zombies collide with the bullet, they dissapear and gets reused, I'm trying to figure out how to replace the zombie sprite with a death animation before that. Should I use the same principle as I did with the flipx or is there other ways to do it?

 

In general, there are to ways to animate sprites in the Intellivision:

  • Cycling GRAM:
    You define a series of pictures that represent the key animation frames for your sprite.  You load the first frame in the sequence to GRAM and point the sprite attribute register to it.  Then, whenever you need to update the sprite animation frame, you load a new picture from the sequence into the same GRAM location (using DEFINE).  You do this periodically, keeping track of the next picture to load, and re-cycling to the first picture when the you reach the end of the sequence.
     
  • Sequencing GRAM:
    You define a series of pictures for the animation, just like with Cycling GRAM, but you load them all continuously into GRAM at the beginning of the game (or level).  Then, whenever you need to change the animation frame, you update the picture pointer in the sprite’s attribute register.  You continue this process, keeping track of the GRAM picture index and advancing the pointer periodically, and re-cycle it back to the first picture, as necessary.

 

Both of these are very effective, and can be used to animate background elements in the BACKTAB as well; although the first technique (Cycling GRAM) becomes impractical when you want to animate more than two sprites or background elements in the same frame.  This is because you can only use one DEFINE and DEFINE ALTERNATE statements to load GRAM pictures per frame.

 

The advantage of Cycling GRAM is that it uses one (or at most two, depending on the sprite resolution) slots in GRAM, leaving the rest for background detail or other sprites.  Also, it does not matter how long your animation sequence is, since it will always consumes the same amount of GRAM space.

 

The main disadvantages are the limitations of DEFINE, as mentioned above; and that loading pictures into GRAM can get expensive, CPU time-wise.

 

The advantage of Sequencing GRAM is that it is very fast (a simple sprite register update), can be done for all sprites on the same frame, and in fact, you can use it to animate background elements as well with very little cost.

 

The primary disadvantage is that it consumes one (or two) slots of GRAM for each picture in the sequence.  So animating multiple sprites and background elements with long animation sequences can eat up a big chunk of GRAM, reducing the space available for background details and sprites.

 

I hope this helps.

 

   dZ.

Link to comment
Share on other sites

I got the animation working.

 

More questions!: I have a counter that goes up each time a zombie is shot, after a certain number, let's say 10. Once the counter reaches the target number the game pauses, a musical jingle plays and the game shows a different screen.

 

1.Pausing the game I guess is as simple as simply disabling input from the player and disabling all variables handling the zombie movement and bullet movement?

 

2.Music I need to learn how that functions. Do you have any resource on the topic of music?

 

3.Going to another screen I suspect is simply making another loop outside the gameloop and simply use a GOTO?

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...