Thursday, June 28, 2007

XeO3: More scripting.

Okay, rather than reply to the comments, I'll write a bit more about it here. The actual loop for processing scripts is pretty small, and really easy to follow - so here it is....


;************************************************************
; *
; Name : ProcessPaths *
; Function: Loop through all our paths and move the *
; objects assosiated with them. This includes *
; the master "SPAWN" path. *
; *
;************************************************************
ProcessPaths ; must start at 0 and go up
ldx #0 ; This means paths being started by the MASTER PATH
ProcessNextPath ; are not delayed a game cycle

lda PathsInUse,x ; path in use? Keep it a byte to make it quick....
beq PathNotActive

lda PathAddressLo,x ; Get baddie current path address/location
sta PathAddress
lda PathAddressHi,x
sta PathAddress+1


;
; Jump back to here to execute another command on the same sprite!
; "some" commands need to be free, which others will take a game tick.
; Changing object attribute or animation are "FREE", while movement is 1 tick.
;
DoNextCommand
ldy #0 ; reset index into command
lda (PathAddress),y ; get command
asl a ; *2 to index table
tay
lda PathJumpTable,y
sta DoJumpHere+1
lda PathJumpTable+1,y
sta DoJumpHere+2

ldy #0 ; point to first byte again
DoJumpHere jmp $0101


;
; once we've finished processing THIS sprite, jump to here
; we can then animate and do any "special" checking that needs to be done.
;
DoNextPath
jsr DoMisc ; 1 scanline wasted doing jsr/ret's.... could make it quicker... or inline it.

lda PathAddress ; Save the path location back into the sprite system
sta PathAddressLo,x
lda PathAddress+1
sta PathAddressHi,x

inx
cpx #MaxPaths
bne ProcessNextPath
ExitPathSystem
rts

PathNotActive:
cpx #0
beq SkipNuke
sta SY,x ; if path not active - nuke Sprite coordinate!
SkipNuke:
inx
cpx #MaxPaths
bne ProcessNextPath
rts



Theres a couple of places where I could save a little, and I may later on, but I only really do that when its not going to change much; updating highly optimised code is next to impossible.

DoMisc does things like copy the baddie coords over to the sprite system (the sprites are independent and not tied into the paths since I want to be able to take one and not the other to another game). DoMisc also does things like checks for Kill when clipped options, and updates sprite centers so that collision routines don't have to keep doing that. It also deals with baddie animation, and getting the address of sprite shapes etc.

As for the Circle routine - thats pretty big... Aside from working out the next set of offsets, its just a MoveABS style function with a counter, so I'll show you the actual GetOffsets function. Theres 2 phases to the circle command - the 1st is a reverse calculation that works out the center for you, allowing you to specify the radius of an arc you are already on - this is a huge bonus, and all that I do is work out 180 degrees further on, and revese the add/subtract to the deltas the 1st time in.


;*****************************************************************************
; workout offsets using path Radius and angle values.
; Store in zero page "CircleOff?" variables
;
; We use an inline macro for the xply as 2*9 cycles * 9 sprites=162 cycles!
; (or a couple of scanlines), and its only 66 bytes each...
;
;*****************************************************************************
GetOffsets
;
; Do RadiusY*sin(AngleY)
;
ldy PathAngleY,x ; get angle
lda SineWave,y ; get "sin(angle)"
sta xplyd ; store
lda PathRadY,x
sta xplyc
XPLY_M ; Use inline macro to avoid the 9 cycles for call/ret.
sta CircleOffY

ldy PathAngleY,x ; angle >180 deg? if so then NEGATE value
bpl !NotNegY
eor #$ff
clc
adc #1
sta CircleOffY
!NotNegY:

;
; Do RadiusX*sin(AngleX).
; (angle has been ofset 90 degrees (64bytes) to account for COS->SIN translation)
;
lda #0
sta CircleOffX+1 ; high byte of X offset

ldy PathAngleX,x ; get angle
lda SineWave,y ; get "sin(angle)"
sta xplyd ; store
lda PathRadX,x
sta xplyc
XPLY_M ; Use inline macro to avoid the 9 cycles for call/ret.
sta CircleOffX

ldy PathAngleX,x ; angle >180 deg? if so then NEGATE value
bpl !NotNegX
eor #$ff
clc ; NEG a
adc #1
sta CircleOffX
bcs !NotNegX
dec CircleOffX+1 ; take high byte it from 0 to $ff
!NotNegX:
rts




I haven't really looked into optimising this that much yet as most of the time is spent doing the multiplys, and they are as fast as they can be.

So this function returns a set of signed deltas from the circle origin, and the main function adds them onto the center and sets the sprites location. Its a pretty expensive function, but all the smooth wavey paths in XeO3 are done with this, it also saves huge amounts of SCRIPTing memory as one command can control a sprite for a long time. The last part is to increment the current angle, allowing the baddie to move in an arc (or more depending on the radius of X and Y),

The scripting also allows for what I call drift... this means I can drift the sprite on X by a certain amount. A drift of 1 means that if I pause, the sprite will move with the scrolling background without needing more commands. Now, if you apply drift to a circle, you can have a zero radius on X, and wobble it vertically, this allows you to make a simple snake wave. Theres lots of tricks you can use with command combinations to get some cool effects.

I did a shoot-em-up on a phone where I allowed the center of a circle to be attached to a parent, which allowed for heirarchial rotation - small orbs spining around a larger orb - very cool. The plus/4 isn't quite able to handle that I think, but that doesn't mean you cant get some nice paths out of it!

If I've missed anything, or if somethings not clear - just ask again! :)

EDIT: Oh - and I dont have 360 degrees, but 256. 128 = 180, 64 = 90 and so on...

3 comments:

Anonymous said...

And here I was, thinking that wavy pattern was alternating half circles... Having separate multipliers for different axes is really smart.

I wonder what you would get if you swept CIRCLE radius from 0 to MAX and back when script is active, with X & Y radius in different phase? Either that would be really cool pattern or it would look like enemies had a drink or two too many ;)


--
TNT

Mike said...

I use that a lot for "random" wavy patterns, baddies just seem to fly all over the place, although after a while you spot the pattern :)

You get patterns like this:

http://www.edwardpultar.com/writings/hands.jpg

http://www.efg2.com/Lab/Library/Delphi/Graphics/Butterfly.gif

http://www.zefdamen.nl/CropCircles/Reconstructions/2000/Wrotham00/Wrotham.gif

and this one...
http://mcraefamily.com/MathHelp/BorromeanRings3d.jpg
if you imagine going smoothly from one hoop to the next!

Mike said...

Oh...I dont have support to animate the radius in this one, but I guess once the source is released you can add anything :)