Jump to content
IGNORED

Newbie gravity experiment


Vincehood

Recommended Posts

Hello,

I am experimenting with some basic gravity simulation but I am struggling...

 

I am attempting to use a friction variable to simulate the energy loss due to friction when bouncing. With a friction value inferior to 1, the ball should gradually bounce less and eventually stop. However I do not get the wanted effect; in such case, the ball stops immediately.

 

I assume this is due to my lack of knowledge about how multiplication works in Intybasic. Additionally I think I don't use properly PRINT to display the velocity (#dy)... since I get some jam (but this is not the core issue).

 

I include the code sample  (friction is set to 1 there so the ball bounces indefinitely - I have commented away the 0.9 value)

 

Am I approaching the problem in a totally wrong way?

 

Thanks

 

gravity.bas

Link to comment
Share on other sites

I just tried it ... SPLAT!  Hehehe.

 

It seems that IntyBASIC does not support floating point values, and that fractions are stored as fixed point numbers for a specific case.  The manual references this post requesting the feature from IntyBASIC:

 

So it seems you can't use fractions in that way.

 

     -dZ.

Link to comment
Share on other sites

I tried to do an experiment using fixed-point arithmetic, but there seems to be something wonky going on with the way IntyBASIC handles the unary negation.  For the following statement:

#dy = -#dy +.#friction

You get the following assembly code:

                                	;[55]   #dy = -#dy +.#friction
                                	SRCFILE "gravity.bas",55
5217   0280 0308                	MVI var_&DY,R0
5219   0300 0309                	SUB var_&FRICTION,R0
521B   0020                     	NEGR R0
521C   0028                     	ADCR R0
521D   0240 0308                	MVO R0,var_&DY

 

It seems it tries to do the normal arithmetic and then just bolt on an ADCR to add the carry afterwards, except that when the term includes negation, it is smart to turn the addition into subtraction, but not smart enough to notice that following it with a NEGR will lose the carry.

 

      -dZ.

Link to comment
Share on other sites

OK, I am afraid I don't follow, my assembler memories have been gone for a long time :)

Otherwise, I experimented a bit further by adding a vertical force which opposes gravity (it is triggered when pushing the side buttons).

Not sure it reacts when I want but the animation is ok. Tried to play with FRAME to get the right speed.

Some tests are missing (the ball can still leave the screen by the top)

 

gravity2.bas

Link to comment
Share on other sites

18 minutes ago, Vincehood said:

OK, I am afraid I don't follow, my assembler memories have been gone for a long time :)

 

If you use integers, you are confined to moving at whole multiples of frames.

 

If you want to use fractions, you're limited to fixed point arithmetic, which treats each 16-bit word as a fixed point decimal, where one byte is the whole part, and another part represents the fractional, scaled to a power of 2.

 

What that means is that you cannot use negative numbers in fixed point fractional values in IntyBASIC.

 

So, how do you switch directions if you cannot make the acceleration variable negative?  You will have to keep track of the direction in some other way, and change your code to subtract when going up, and add when going down.

 

 

Quote

Otherwise, I experimented a bit further by adding a vertical force which opposes gravity (it is triggered when pushing the side buttons).

Not sure it reacts when I want but the animation is ok. Tried to play with FRAME to get the right speed.

Some tests are missing (the ball can still leave the screen by the top)

 

gravity2.bas 1.21 kB · 1 download


I'll try it out.

Edited by DZ-Jay
Link to comment
Share on other sites

5 hours ago, DZ-Jay said:

 

If you use integers, you are confined to moving at whole multiples of frames.

 

If you want to use fractions, you're limited to fixed point arithmetic, which treats each 16-bit word as a fixed point decimal, where one byte is the whole part, and another part represents the fractional, scaled to a power of 2.

 

What that means is that you cannot use negative numbers in fixed point fractional values in IntyBASIC.

 

So, how do you switch directions if you cannot make the acceleration variable negative?  You will have to keep track of the direction in some other way, and change your code to subtract when going up, and add when going down.

I get what you mean!

 

On a different topic, I was trying in this example to avoid abusing of WAIT according to Intybasic principles read in other threads. However I still have one WAIT in the main loop (otherwise the animation looks pretty bad). It might be ok for such a small program that doesn't do much but I am wondering if it is really the right pattern for a bigger and more complex game...

Link to comment
Share on other sites

Time passes (and the raster beam moves) during execution even without WAIT. It is used to synchronize with the vertical blank, to make sure that certain actions have taken place before the next instruction, e.g. defining GRAM, moving sprites, scrolling, possibly reading controllers. You can use it to generally slow down your loop too, but I'd try to limit it to code that does something where it is required. From what I understand, some of these actions are "buffered" so you can prepend a few actions and then one WAIT for vertical blank before they all trigger, instead of WAIT every time. Practical testing will tell what and how things work.

Link to comment
Share on other sites

4 hours ago, Vincehood said:

I get what you mean!

 

On a different topic, I was trying in this example to avoid abusing of WAIT according to Intybasic principles read in other threads. However I still have one WAIT in the main loop (otherwise the animation looks pretty bad). It might be ok for such a small program that doesn't do much but I am wondering if it is really the right pattern for a bigger and more complex game...

To add to what @carlsson said, the better pattern, in my opinion, is to use ON FRAME GOSUB instead of WAIT.

 

What WAIT does is to loop in place waiting for the next interrupt request to draw the next frame, so that you can synchronize your program with the Vertical Blanking (VBLANK)  interrupt, which is the only source of constant timing on the Intellivision.

 

This has two purposes:  First, it maintains the pace of the program, so that your game can be treated as a series of repeatable cycles.  Second, it gives you access to the STIC video controller to update sprites and graphics RAM, which are only accessible for a brief period on each video frame.

 

A typical game engine may look like this:

ON FRAME GOSUB UpdateStuff

Engine:
  ' Move enemies
  ' Move player
  ' Check collisions
  ' do other interesting gamey stuff
  WAIT
  GOTO Engine

UodateStuff:
  ' Update sprites positions
  ' Update animation graphics
  ' Do other time critical stuff
  End


Then again, I'm not really an IntyBASIC programmer, so others that actually use it may have different opinions. ;)

   dZ.

  • Like 1
Link to comment
Share on other sites

8 minutes ago, carlsson said:

ON FRAME is something I've read about and seen in Oscar's book but I never got into the mindset to use it. Probably that is a very good suggestion to learn how to do things in the right way from the beginning.

That's why I put the caveat at the end of my comment:  I'm coming to this as an outsider, not being very familiar with the patterns that you all use.

 

I am aware that a lot of you started using IntyBASIC from its humble beginnings, back when it was still very rough around the edges, and a lot of the "good stuff" was yet to be developed.  So, a lot of what I would consider as "anti-patterns" were entrenched.

 

Then again, that's the beauty of the language:  you can still express your game logic in myriad ways, and it enables hobbyists and non-engineery types to be creative. :)
 

I'm just too obsessive with rigid structure, reusable patterns, and abstraction frameworks.  I suck at just "whipping something up" on a weekend like some of you do.

Edited by DZ-Jay
Link to comment
Share on other sites

3 hours ago, Vincehood said:

Thanks DZ-Jay & carlsson, I will experiment with ON FRAME.

A few caveats about ON FRAME GOSUB:

  • It is a language directive, not a statement or command.  This means that you can only have one in the program, which makes sense since there should only be a single event handler for the interrupt signal.  If you put multiple ON FRAME GOSUBs, the last one will be used.
  • It runs after sprite and GRAM updates and all critical core functions are run.  This means that updating sprites at this point will set them up for the next video frame.

 

I was just looking at the source code of the IntyBASIC runtime engine and realized that ON FRAME GOSUB does not work like I thought it did.  It hooks into the internal interrupt service routine -- but at the end.  That means that by the time it calls your subroutine, the video and audio hardware has been updated with the previous known state, GRAM has been loaded, and sprite attributes have been updated.

 

ON FRAME GOSUB gives you a source of timing, so there's that.  The common pattern I see from others is to update the sound device to process sound effects.  This works because the sound chip is accessible at any time (unlike the video chip) and therefore it makes sense to keep them updated on every frame.

 

All that said, ON FRAME GOSUB is no different from using WAIT in that regard:  your code executes not at the moment the interrupt occurs at the top of the VBLANK period, but after all core functions are run by the IntyBASIC runtime engine.  In fact, ON FRAME GOSUB is slightly better, since it runs before returning to the WAIT caller.

 

I guess there is no point in updating sprites and graphics in the ON FRAME GOSUB, since it'll have to wait until the next frame to actually apply changes.  You are better off putting those in your game loop, right after updating the logical state of the game world.  That way, at least you know that the world state is ready for the next frame.  :)

 

I do not know to what extent the information above is useful to you, but I thought I'd share it. 

 

    -dZ.

Link to comment
Share on other sites

ok, it makes sense, thanks!

I had meanwhile restructured a bit the program based on previous advices and GOSUB usage. I could easily change the high level structure.

I get anyway an OK effect with the ball. I had also started to apply gravity depending on the frame to slow down things a bit

(see gravity4.bas).

Then I decided to replace the ball by a carlsson bird (which I found in some other thread :)), so I get to play a bit more with animation.

Similar to the ball, the bird should be flying up & animate when pushing the side buttons. I also updated some tests but I am running into other issues. I think my brain is no longer helping me & I will need to check the code again with a fresh mind. See gravity5.bas.

In such situation, I realise I am pretty bad at debugging. For someone not into assembler like me, is there a more high level way to debug based on logging, breaking points or whatever?

 

thanks!

 

gravity4.bas gravity5.bas

Link to comment
Share on other sites

2 hours ago, Vincehood said:

In such situation, I realise I am pretty bad at debugging. For someone not into assembler like me, is there a more high level way to debug based on logging, breaking points or whatever?

The emulator is also a debugger.  If you are using the IntyBASIC SDK, then you can just use the "intydbg" command to enable the debugger.  You can then review the included documentation on the SDK tools for information on how to use the debugger.

 

If not, you can use the "-d" option on the command line when invoking the emulator to enable the debugger.  Review the jzIntv documentation to learn about the debugger commands.

 

All that said, I'm afraid that the debugger is a command-line tool, not a graphical interface.  IntyBASIC does create a map of the assembly symbol table and the BASIC source that the debugger can use so that you could see the BASIC source-level code during debugging, and call your variables by name.  The IntyBASIC SDK command "intydbg" sets that up automatically for you.

 

Here are some useful commands you can use from the debugger prompt:

?                       = View debugger command help
b <label or address>    = Set or clear breakpoint at label or address
w <variable or address> = Set or clear watch on writes to variable or address
r                       = Run (return to emulation)
<Enter>                 = Step (execute one opcode)
m <variable or address> = Display the memory contents of variable or address (like PEEK)
p <#1> <#2>             = "Pokes" value #2 into variable or address #1

 

There are plenty more!

 

    -dZ.

Edited by DZ-Jay
Link to comment
Share on other sites

And now, I remember something. 3 years ago when I was working on my Tron remake in Windows environment, I was actually using your SDK. I did not use the debug features then.

I am nowadays coding on Mac & I must have missed that your SDK is also available in this environment, great :)

 

Link to comment
Share on other sites

13 minutes ago, Vincehood said:

Oh! Sounds great, I was only using the base intybasic compiler. Will install all of that and try out! Thanks a lot!

Well, the compiler (intybasic) and emulator (jzintv) do everything themselves, so you got that already.  It's just that you have to manually compile, then assemble, then run the symbol mapper; and then put it all together in a command-line to run the emulator with the debugger enabled.

 

That's not really a problem, and some people just create scripts to do it for them, and it's all fine.

 

What the IntyBASIC SDK does is provide a set of such scripts already ready for you.  So you get different tools that each invoke the compiler, assembler, emulator, etc. in the right way and puts the necessary incantations together in their command-lines, accordingly.  Veterans like having the full control over everything, but the SDK can newbies and lazy people (like me!) who don't want to do it all by hand.  It just makes life easier, in my opinion.

 

The SDK has the following tools:

  • INTYNEW - create a new IntyBASIC SDK project with standard boilerplate source file.
  • INTYBUILD - compile, assemble, and map the project source code into an executable ROM.
  • INTYRUN - runs an IntyBASIC SDK project on the emulator.
  • INTYDBUG - runs an IntyBASIC SDK project on the emulator with the debugger on.
  • INTYTESt - combines INTYBUILD and INTYDBUG in one step, so you can compile, assemble, and map the source code, then run the emulator with the debugger on at once.

It started as a community project to make IntyBASIC more attractive to newcomers.  I've been maintaining it for the past couple of years.

 

You can find it here:

 

 

     -dZ.

Link to comment
Share on other sites

Just now, Vincehood said:

And now, I remember something. 3 years ago when I was working on my Tron remake in Windows environment, I was actually using your SDK. I did not use the debug features then.

I am nowadays coding on Mac & I must have missed that your SDK is also available in this environment, great :)

 

Yes, but unfortunately, there's no automatic installer.  That said, it's pretty straightforward to "install": just copy the folder to your ~/Documents folder, and open up the Terminal window. :)

 

Let me know if you have any questions or problems.  And don't forget to read the included documentation, which shows you how to use the tools.

 

    -dZ.

  • Thanks 1
Link to comment
Share on other sites

9 minutes ago, Vincehood said:

I started the debugger in stop mode: intydbug -s

I am trying to access some of my program variables but how exactly do you use the m command?

 

m #dy

Could not find symbol named '#dy'

IntyBASIC "mangles" the names internally to ensure their uniqueness and avoid any collisions with names in assembly modules (which, for obvious reasons, are hidden from IntyBASIC).

 

The debugger only sees the final internal names, but it is easy to figure them out, just follow these rules:

  • Convert name to upper-case;
  • prefix the name with "var_";
  • replace the "#" with an "&".

So, in your example, to "peek" at the value held by variable "#dy," you use:

m var_&DY

Notice that the "#" for 16-bit variables is replaced by an "&", the name was capitalized, and then prefixed by "var_".

 

In the case of 8-bit variables, there is no "#" to convert, but the rest still applies.

 

For labels, in case you want to add breakpoints, the names are transformed like this:

  • Remove the colon;
  • Convert to upper-case;
  • prefix with "label_".

So to put a breakpoint in procedure "bouncing," you use:

b label_BOUNCING

 

That's it.  I know it is a bit confusing, but it's straightforward and simple to remember.

 

I'll probably write a simple "debugging guide" for the SDK's next release this year.

 

     -dZ.

Edited by DZ-Jay
  • Thanks 1
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...