Sprite Wrapping

Made something in Assembly? Show it off, and tell us how it works...
Absolute beginners welcome... we want to see what you've done!
Forum rules
There is a special rule on this forum...
This forum is intended to offer equal amounts encouragement and constructive feedback...
Therefore, if you say 2 negative things about someones work, you must think of 2 or more equally positive things...

Eg: "Great first effort, the idea is absolutely fascinating... However I noticed a few bugs, and maybe the graphics could be improved..."

If you can't think of anything good to say, then don't say anything!

If we don't encourage newbie programmers they won't have the confidence and motivation to stick at it and become great programmers! *speaking from experience*
Post Reply
Indyuk2000
Posts: 10
Joined: Wed Apr 24, 2019 11:59 pm

Sprite Wrapping

Post by Indyuk2000 » Fri Aug 02, 2019 2:15 pm

Hi All,

I've been working on learning how to wrap sprites around the screen. The effect being that when the sprite starts to leave one side of the screen it begins to appear on the opposite.

My example code works in the y-axis (I think x-axis is going to be tricky) and moves 1 pixel at a time either upwards or downwards. To change direction just press the D key. I'm sure it's not the best or smartess solution but, it works. Hope you like it.

Code: Select all

xmin equ 0
xmax equ 319

ymin equ 0
ymax equ 199

spriteH equ 18			;value in total scanlines i.e pixel height
spriteW equ 4			;value in bytes in the x axis on one scanline

org &4000
	
	ld a,1			;screen mode
	call &BC0E

	ld b,0			;screen background colour
	ld c,b
	call &BC32

	ld b,2			;screen border colour
	ld c,b
	call &BC38

		
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mainloop			;start of main loop

	ld a,66			;ESC key. 66=ESC key code
	call &BB1E		;key press check	
	jp nz,finish		;if ESC key pressed, exit to BASIC
	
	ld a,61			;D key 
	call &BB1E		;key press check
	call nz,changedir	;if pressed, perform vertical direction change

	call updateplayermovements
	
	call drawscreenobjects		

	call &BD19 		;wait for v-sync
	
	;call &bb18		;wait for any key press

	jr mainloop

finish:
	ret			;back to BASIC	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

updateplayermovements
	
	call updatespritepos	;update new sprite x/y position
	
	ret

drawscreenobjects
	
	call drawplayer
	
	ret

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

changedir:
	
	ld a,(dir)		;get current dir flag
	xor 1			;flip the 1 to 0 or vice versa to change directional flag
	ld (dir),a		;save flag back to memory
	
	ret

updatespritepos:

	ld hl,(ypos)		;get current ypos
	
	ld a,(dir)		;load direction flag
		
	cp 0			;if it's 0 then we go in a downwards direction
	jr z,_sub 
			
	cp 1			;if it's 1 then we go in an upwards direction
	jr z,_add

	ret			;don't really need this return but, just in case something slips through

  _sub	
	ld a,l			;get current ypos	

	cp ymin			;have we reached bottom of screen?
	jr z,__resettotop	;if we have, reset pos to top	
	
	dec hl			;decrement ypos by one
	jr _save

  __resettotop
	ld hl,ymax		;set ypos to ymax amount
	jr _save	

  _add	
	ld a,l			;get current ypos

	cp ymax			;have we reached top of screen?
	jr z,__resettobottom	;if we have, reset pos to bottom

	inc hl			;increment ypos by one
	jr _save

  __resettobottom
	ld hl,ymin		;set ypos to ymin amount
	jr _save

  _save	
	ld (ypos),hl		;save new ypos		 

	ret

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


drawplayer:

	ld de,(xpos)		;load in current xpos in pixels
	ld hl,(ypos)		;load in current ypos in pixels
	
	call &bc1d		;convert x/y to screen address, which gets saved in HL. AF & DE get corrupt

	ld (scrmempos),hl 	;save copy of the initial start screen address 
				
				;The lines below are like a function call whereby we pass it 3 parameters
				;
	ld hl, sprite_bar	;param 1 - load in sprite data 
	ld de,(scrmempos)	;param 2 - load in screen start memory pos
	ld a, spriteH		;param 3 - height of sprite (in pixel lines)	

	call drawobject		;call the function to draw the sprite

	ret



drawobject:
	
	;Entry needs to be as follows;
	;
	; HL = Sprite data
	; DE = Screen start address
	; A  = Sprite height in pixels
	
_plotline
	ld bc,spriteW		;max horizontal byte count for scanline (loop)
	push af			;save the sprite height count
	and c			;reset the Zero flag because we need to test against it to
				;see if we have reached the maximum horizontal byte count of the scanline

	ldir			;blit the scanline data to the screen
	
	pop af			;read back the height counter i.e current value

	dec a			;reduce the height loop counter by one
	cp &0			;has the counter reached Zero?
	ret z			;if yes, the sprite has been fully plotted, return to calling function
	push af			;if not, save the current scanline counter value so we don't loose track.. 
				;..of which scanline we're at in the sprite plot

__updatescradd:			;this block is to move to the next (sprite) scanline

	ex de,hl		;we need to swap the two values around because we need DE's value in HL
	ld hl,(scrmempos)	;get last scanline start address

___bottomcheck:

	ld a,h			;lets check if the new scanline has reached the bottom of the screen
		
	cp &ff			;if the new value is &FF then we have reached the bottom screen boundary
				
	jp nz,_ok		;we use the Z flag to decide what we need to do based on the evaluation above
				;if we haven't hit the bottom then skip rest of the code and check if we hit top
				;of screen

	ld a,l			;if the first check has been met then we have to check the low byte as well
	cp &80			;we compare this to &80 as that is the beginning of the last scanline low byte
	
	jp c,_ok		;we check against the C flag as we want to perform a greater than check..
				;..because the value can potentially be between &80 and &CF i.e
				;left hand side of the screen to the right hand side of the screen
				;again, if this condition is not met then resume as normal

	;the block below does the sprite "wrap" by subtracting &3F80 (amount to take in back to the top)
	;from current scanline address
	ld a,h			;load H value into A
	sub a,&3f		;subtract &3F 	
	ld h,a			;save new H value back
	ld a,l			;load L into A
	sub a,&80		;subtract &80
	ld l,a			;save new L value back

	jr _ok

___topcheck:

	ld a,h			;lets check if the new scanline will exceed the bottom of the screen	
	cp &c0			;if the new value is &00 then we have crossed the bottom screen boundary
				
	jp nz,_ok		;we use the Z flag to decide what we need to do based on the evaluation above
				;if we haven't hit the bottom then skip rest of the code and resume as normal
	ld a,l			;if the first check has been met then we have to check the low byte as well	
	cp &4f			;we compare this to &80 as that is the beginning of the last scanline low byte
	
	jp nc,_ok		;we check against the C flag as we want to perform a less than check..
				;..because the value can potentially be between &00 and &4F i.e
				;left hand side of the screen to the right hand side of the screen
				;again, if this condition is not met then resume as normal

	;the block below does the sprite "wrap" by subtracting &3F80 (amount to take in back to the top)
	;from current scanline address
	ld a,h			;load H value into A
	add a,&3f		;subtract &3F 	
	ld h,a			;save new H value back
	ld a,l			;load L into A
	add a,&80		;subtract &80
	ld l,a	


_ok:	
	call &bc26 		;get next scanline start address based on current HL value. AF gets corrupt

	pop af			;restore the scanline counter value
	ld (scrmempos),hl	;save new scanline address for use in the blit 
	ex de,hl		;(re)swap the values around

	jr _plotline		;plot scanline

	ret




;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;various object declarations/definitions
xpos:		defw 150 		;sprite start x-pos
ypos:		defw spriteH 		;sprite start y-pos
dir:		defb 1			;player direction flag 0=downwards 1=upwards

scrmempos:	defw 1			;used to store screen memory start location.
					;this is important as the whole sprite print logic works
					;relative to this
nolist

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;various sprite data

sprite_Bar:
defb &00,&00,&00,&00; line 0
defb &00,&FF,&FF,&00; line 1
defb &00,&FF,&FF,&00; line 1
defb &33,&FF,&ff,&cc; line 3
defb &33,&FF,&FF,&cc; line 4
defb &00,&FF,&FF,&00; line 2
defb &00,&FF,&FF,&00; line 2
defb &00,&FF,&FF,&00; line 2
defb &00,&FF,&FF,&00; line 2
defb &00,&FF,&FF,&00; line 2
defb &00,&FF,&FF,&00; line 2
defb &00,&FF,&FF,&00; line 2
defb &00,&FF,&FF,&00; line 2
defb &33,&FF,&ff,&cc; line 3
defb &33,&FF,&FF,&cc; line 4
defb &00,&FF,&FF,&00; line 5
defb &00,&FF,&FF,&00; line 6
defb &00,&00,&00,&00; line 7 

User avatar
akuyou
Posts: 563
Joined: Mon Apr 22, 2019 3:19 am
Contact:

Re: Sprite Wrapping

Post by akuyou » Sat Aug 03, 2019 10:43 pm

Great work, I tried the code, and of course it works great!

You've done really well on this... I had one heck of a time writing the 'clipping' code for the sprites in chibiakumas, I'm not going to lie, it was pretty much trial and error in the end due to my crappy maths ability!

You're right, I guess doing it on the X-axis will be harder, but I'm pretty sure if you've got this far you'll manage it fine.

Your code is very neat and well documented, far better than any of mine! really well commented!
Chibi Akuma(s) Comedy-Horror 8-bit Bullet Hell shooter! // 「チビ悪魔」可笑しいゴシックSTG ! // Work in Progress: ChibiAliens

Interested in CPU's :Z80,6502,68000,6809,ARM,8086,RISC-V
Learning: 65816,ARM,8086,6809

Indyuk2000
Posts: 10
Joined: Wed Apr 24, 2019 11:59 pm

Re: Sprite Wrapping

Post by Indyuk2000 » Sun Aug 04, 2019 7:33 pm

Hi Keith,

Glad you liked it. I appreciate your words of encoragement. My goal is to learn code (no doubt thanks to you) and try and share with other users what I have learnt in a way of producing some simple programs. There's no doubt that it took me a while to suss out the logic for the wrapping. All I can say is thank heavens for the debugger in WinApe. Without that I would have probably given up.

There's two other things I'm going to try and add to this and that is the ability to add multiple sprites so we can have more than one object on the screen and, also the ability to erase the sprite before plotting new position. At present you may have noticed that I'm cheating and not actually erasing the sprite, instead rely on the two blank lines I have at the top and bottom of the sprite data to clear as it goes along. :D

What technique would you suggest I can use to have an array of objects? I was thinking something along the lines;

Code: Select all

aliens:
;byte 1 = alive/dead 1=alive 0=dead
;byte 2 = x pos
;byte 3 = y pos
;byte 4 = screen address pos
db	1,1,199,&c000
...
...
...
and then load that into the IX register and increment through the bytes setting/updating the object data and plotting the sprite.

Also with the erasing of the sprite I was thinking of adopting a technique whereby I keep a copy of the background bytes that were overwritten when the sprite got printed. This seems to be quite a popular method without using a second buffer.

What do you think? Am I heading in the right direction?

Thanks

User avatar
akuyou
Posts: 563
Joined: Mon Apr 22, 2019 3:19 am
Contact:

Re: Sprite Wrapping

Post by akuyou » Sun Aug 04, 2019 9:36 pm

I think your suggestion of a table of sprites looks like a good one... of course if you're not double buffering, then there will be no need to redraw sprites that have not moved, or had anything overlap - so maybe a 'redraw flag' or a 'width & height / MaxX & MaxY' to work out which sprites you need to redraw , but it may be easier to redraw them all if speed isn't an issue - whats best will depend on what your game is, how many onscreen sprites there will be, and how fast you're trying to make it

I can think of three ways you can deal with not clearing the background

1. XOR sprites - these will invert anything non color 0 behind them, so you just need to draw one twice to undo it, they can make a mess but can work really well - see the game 'Island of dr Destructo'

2.Store background into a buffer - and draw it again to undo the sprite - this is a good idea, but you'll need to think how you want to draw them, are you gong to clear all the sprites in one go, then redraw them separately (could make sprites flicker), or just clear each sprite and redraw them one by one (could cause sprites that overlap to get odd)

3. Just recalculate the background - rather than a buffer - just draw the background again and whatever tiles/sprites were in the position under the sprite - I think this is the most common solution used in games that are single buffered... I mentioned a sprite 'MaxX & MaxY' earlier, I think this may be the key to creating code that can completely 'redraw' a small area of the screen - I've not written any such code myself, but I think this is what I would do if I was looking to create a single buffered action game.

Of course ChibiAkumas, being double buffered, didn't have this problem, the whole background and sprites were completely redrawn every frame - but that slows things down, and uses 32k of the 64k ram, so it's not ideal either.
Chibi Akuma(s) Comedy-Horror 8-bit Bullet Hell shooter! // 「チビ悪魔」可笑しいゴシックSTG ! // Work in Progress: ChibiAliens

Interested in CPU's :Z80,6502,68000,6809,ARM,8086,RISC-V
Learning: 65816,ARM,8086,6809

Post Reply

Return to “Show and Tell”