├── 01_SimpleScrolling ├── README.md ├── res │ ├── planes │ │ └── plane_a.png │ ├── resources.res │ └── sprites │ │ └── ship.png └── src │ └── main.c ├── EndlessScroll ├── README.md ├── bg.png ├── ceiling.png ├── example │ ├── res │ │ ├── bg │ │ │ └── bg.png │ │ └── resources.res │ └── src │ │ └── main.c ├── image.png ├── main.c.jinja ├── main_wide.c.jinja ├── python_win_install.png ├── resources.res.jinja ├── sgdk_endless_scroll.py ├── test_tile.png ├── test_wood.png ├── tilefloor.png ├── warp_0.png ├── warp_1.png ├── warp_2.png ├── warp_3.png ├── warp_4.png ├── warp_5.png ├── warp_6.png ├── woodceil.png └── woodfloor_tileceiling.png ├── HScroll ├── res │ ├── planes │ │ └── madmarco_bg.png │ └── resources.res └── src │ └── main.c ├── HScrollDec ├── res │ ├── planes │ │ ├── madmarco_bgb.png │ │ └── numbered.png │ └── resources.res └── src │ └── main.c ├── HScrollLine ├── res │ ├── bg │ │ └── desert.png │ └── resources.res └── src │ └── main.c ├── HScrollTiled ├── res │ ├── planes │ │ ├── madmarco_bga.png │ │ └── madmarco_bgb.png │ └── resources.res └── src │ └── main.c ├── LICENSE ├── README.md ├── RotatePy ├── README.md ├── crosshairs.png ├── green_bar.png ├── green_bar_rotated_15.png ├── green_bar_rotated_2.png ├── green_bar_rotated_5_MOAR_ROWS.png ├── green_bar_rotated_5_too_few_rows.png ├── main.c.jinja ├── platform.png ├── points.csv ├── resources.res.jinja ├── rotate_-5_to_5 │ ├── res │ │ ├── bg │ │ │ └── platform.png │ │ └── resources.res │ └── src │ │ ├── main.c │ │ └── rotation.h ├── rotate_2_degrees │ ├── res │ │ ├── bg │ │ │ └── platform.png │ │ └── resources.res │ └── src │ │ ├── main.c │ │ └── rotation.h ├── rotation_and_translation │ ├── res │ │ ├── bg │ │ │ └── platform.png │ │ └── resources.res │ └── src │ │ ├── main.c │ │ └── rotation.h ├── sgdk_scroll_rotate.py └── ship.png ├── Rotation ├── res │ ├── planes │ │ └── boss_truck.png │ └── resources.res └── src │ └── main.c ├── RotationDual ├── res │ ├── bg │ │ ├── planea.png │ │ └── planeb.png │ ├── resources.res │ └── sprites │ │ ├── ship_sheet.png │ │ └── shot_sheet.png └── src │ └── main.c ├── RotationDualPrecalc ├── res │ ├── bg │ │ ├── planea.png │ │ └── planeb.png │ ├── resources.res │ └── sprites │ │ ├── crosshairs.png │ │ └── ship.png └── src │ ├── main.c │ └── rotation.h ├── RotationWithBackground ├── res │ ├── planes │ │ ├── background.png │ │ └── boss_truck.png │ ├── resources.res │ └── sprites │ │ ├── boss_truck_wheel.png │ │ ├── gun_bike_b.png │ │ ├── gun_bike_rider_b.png │ │ └── tree1.png └── src │ └── main.c ├── ScrollingWithCamera ├── res │ ├── planes │ │ └── plane_a.png │ ├── resources.res │ └── sprites │ │ ├── explosion.png │ │ ├── rock.png │ │ ├── ship.png │ │ └── shot_sheet.png └── src │ └── main.c ├── VScrollTiled ├── res │ ├── bg │ │ └── planeb.png │ └── resources.res └── src │ └── main.c └── waves ├── ex1 ├── README.md ├── res │ ├── bg │ │ └── bg.png │ └── resources.res └── src │ └── main.c └── ex2 ├── res ├── bg │ ├── bg.png │ └── fg.png ├── resources.res └── sprites │ └── bubble.png └── src └── main.c /01_SimpleScrolling/README.md: -------------------------------------------------------------------------------- 1 | # Simple Scrolling Example. 2 | 3 | -------------------------------------------------------------------------------- /01_SimpleScrolling/res/planes/plane_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/01_SimpleScrolling/res/planes/plane_a.png -------------------------------------------------------------------------------- /01_SimpleScrolling/res/resources.res: -------------------------------------------------------------------------------- 1 | TILESET plane_a_tileset "planes/plane_a.png" BEST ALL 2 | MAP plane_a_map "planes/plane_a.png" plane_a_tileset BEST 0 3 | 4 | SPRITE ship "sprites/ship.png" 4 4 NONE 5 | 6 | PALETTE plane_palette "planes/plane_a.png" 7 | PALETTE ship_pal "sprites/ship.png" -------------------------------------------------------------------------------- /01_SimpleScrolling/res/sprites/ship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/01_SimpleScrolling/res/sprites/ship.png -------------------------------------------------------------------------------- /01_SimpleScrolling/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | 4 | 5 | // Map Position Variables. These are what matter in this example. 6 | fix32 mapPosX; 7 | fix32 mapPosY; 8 | 9 | 10 | // sprite variables 11 | #define PLAYER_FRAME_COUNT 16 12 | fix32 deltaX[PLAYER_FRAME_COUNT]; 13 | fix32 deltaY[PLAYER_FRAME_COUNT]; 14 | int rotationIndex; 15 | Sprite* player; 16 | fix32 playerRotation; 17 | #define MAX_ROTATION_INDEX FIX32(15) 18 | #define MIN_ROTATION_INDEX FIX32(0) 19 | 20 | 21 | void handleInput() { 22 | u16 value = JOY_readJoypad(JOY_1); 23 | 24 | if( value & BUTTON_LEFT ) { 25 | playerRotation += FIX32( 0.25 ); // using fixpoint to slow down the rotation speed a bit. 26 | if( playerRotation > MAX_ROTATION_INDEX ) { 27 | playerRotation = MIN_ROTATION_INDEX; 28 | } 29 | rotationIndex = fix32ToInt( playerRotation); 30 | SPR_setAnim( player, rotationIndex); 31 | } else if( value & BUTTON_RIGHT ) { 32 | playerRotation -= FIX32( 0.25 ); 33 | if( playerRotation < MIN_ROTATION_INDEX ) { 34 | playerRotation = MAX_ROTATION_INDEX; 35 | } 36 | rotationIndex = fix32ToInt( playerRotation); 37 | SPR_setAnim( player, rotationIndex); 38 | } 39 | 40 | if( value & BUTTON_UP ) { 41 | // pushing UP on the joypad applies motion delta to the map position. 42 | mapPosX = mapPosX + deltaX[rotationIndex]; 43 | if( mapPosX < FIX32(0) ) { 44 | mapPosX = FIX32(0); 45 | }else if( mapPosX > FIX32(6079) ) { // full width of map image. 46 | mapPosX = FIX32(6079); 47 | } 48 | mapPosY = mapPosY + deltaY[rotationIndex]; 49 | if( mapPosY < FIX32(0) ) { 50 | mapPosY = FIX32(0); 51 | }else if( mapPosY > FIX32(2239) ) { // this seems to be as far as I cna go without corrupting the map 52 | mapPosY = FIX32(2239); 53 | } 54 | 55 | } 56 | } 57 | 58 | int main( bool hard ) { 59 | 60 | PAL_setPalette( PAL1, plane_palette.data, CPU); 61 | PAL_setPalette( PAL2, ship_pal.data, CPU ); 62 | 63 | // get our position in VRAM. 64 | int ind = TILE_USER_INDEX; 65 | // Load the plane tiles into VRAM 66 | VDP_loadTileSet( &plane_a_tileset, ind, DMA ); 67 | 68 | // init plane 69 | Map *map_a = MAP_create( &plane_a_map, BG_A, TILE_ATTR_FULL( PAL1, FALSE, FALSE, FALSE, ind ) ); 70 | // set initial map position 71 | mapPosX = FIX32(1440); 72 | mapPosY = FIX32(1120); 73 | 74 | MAP_scrollTo( map_a, fix32ToInt(mapPosX), fix32ToInt(mapPosY ) ); 75 | 76 | 77 | // Init sprite engine with defaults 78 | SPR_init(); 79 | fix32 posX = FIX32( 160 - 16 ); 80 | fix32 posY = FIX32( 112 - 16 ); 81 | player = SPR_addSprite( &ship, fix32ToInt(posX), fix32ToInt(posY), TILE_ATTR(PAL2, 0, FALSE, FALSE )); 82 | playerRotation = MIN_ROTATION_INDEX; 83 | SPR_setAnim( player, fix32ToInt(playerRotation ) ); 84 | 85 | 86 | // setup map motion delta based on index (index is shared with sprite so scrolling motion 87 | // and player direction match) 88 | deltaX[0] = FIX32( 0.000000 ); 89 | deltaY[0] = FIX32( -2.000000 ); 90 | deltaX[1] = FIX32( -0.765367 ); 91 | deltaY[1] = FIX32( -1.847759 ); 92 | deltaX[2] = FIX32( -1.414214 ); 93 | deltaY[2] = FIX32( -1.414214 ); 94 | deltaX[3] = FIX32( -1.847759 ); 95 | deltaY[3] = FIX32( -0.765367 ); 96 | deltaX[4] = FIX32( -2.000000 ); 97 | deltaY[4] = FIX32( -0.000000 ); 98 | deltaX[5] = FIX32( -1.847759 ); 99 | deltaY[5] = FIX32( 0.765367 ); 100 | deltaX[6] = FIX32( -1.414214 ); 101 | deltaY[6] = FIX32( 1.414214 ); 102 | deltaX[7] = FIX32( -0.765367 ); 103 | deltaY[7] = FIX32( 1.847759 ); 104 | deltaX[8] = FIX32( -0.000000 ); 105 | deltaY[8] = FIX32( 2.000000 ); 106 | deltaX[9] = FIX32( 0.765367 ); 107 | deltaY[9] = FIX32( 1.847759 ); 108 | deltaX[10] = FIX32( 1.414214 ); 109 | deltaY[10] = FIX32( 1.414214 ); 110 | deltaX[11] = FIX32( 1.847759 ); 111 | deltaY[11] = FIX32( 0.765367 ); 112 | deltaX[12] = FIX32( 2.000000 ); 113 | deltaY[12] = FIX32( 0.000000 ); 114 | deltaX[13] = FIX32( 1.847759 ); 115 | deltaY[13] = FIX32( -0.765367 ); 116 | deltaX[14] = FIX32( 1.414214 ); 117 | deltaY[14] = FIX32( -1.414214 ); 118 | deltaX[15] = FIX32( 0.765367 ); 119 | deltaY[15] = FIX32( -1.847759 ); 120 | 121 | 122 | while(TRUE) { 123 | // Read Joypad 124 | handleInput(); 125 | 126 | // Update the sprite 127 | SPR_update(); 128 | 129 | // Set the scrolling position 130 | MAP_scrollTo( map_a, fix32ToInt(mapPosX), fix32ToInt(mapPosY ) ); 131 | 132 | 133 | SYS_doVBlankProcess(); 134 | } 135 | return 0; 136 | } 137 | 138 | -------------------------------------------------------------------------------- /EndlessScroll/README.md: -------------------------------------------------------------------------------- 1 | # Endless Scroll Image Generation Script 2 | 3 | `sgdk_endless_scroll.py` is a simple Python script that creates endless 4 | scrolling images for use with Sega Genesis / SGDK. You must supply at least 5 | one 16-color indexed image to be warped into a repeating floor pattern. The 6 | generated image can optionally have a ceiling pattern. The ceiling can use 7 | the same image as the floor or a separate image can supplied for the ceiling 8 | pattern. 9 | 10 | ## TODO 11 | The script has some rough edges, but does what I needed it to. There are 12 | some obvious things that could be improved: 13 | 14 | * Check input values for invalid combinations 15 | * Blend pixels at edges of warp pattern (or sample images instead of 16 | using OpenCV's warp) 17 | * Generate code with user configurable scrolling step sizes 18 | * Generate code with lookup tables for scrolling values 19 | 20 | I'll likely add code to check input values and handle odd numbers, but this 21 | script is very low priority. I may never get around to the rest of it. 22 | 23 | 24 | ## Dependencies 25 | The script was written for [Python3](https://www.python.org/downloads/) and requires 26 | [NumPy](https://numpy.org/), [PILLOW](https://python-pillow.org/), and 27 | [OpenCV](https://opencv.org/). 28 | 29 | I'm using WSL and Ubuntu 22.02 on my development machine, but I imagine 30 | most people will be using Windows. I'm going to keep on using WSL, but 31 | I have installed the windows versions to test the script. 32 | 1. Get Python3. I used the Microsoft Store version. 33 | 34 | 2. Install the dependencies. I opened a command line window and typed: 35 | ```cmd 36 | pip3 install numpy 37 | pip3 install pillow 38 | pip3 install opencv-python 39 | pip3 install jinja2 40 | ``` 41 | 3. Run the script with: 42 | ```cmd 43 | python3 sgdk_endless_scroll.py 44 | ``` 45 | 46 | # Basic usage 47 | 48 | ```bash 49 | usage: sgdk_endless_scroll.py [-h] [-v] [-f ARG] [-n ARG] [-s ARG] [-e ARG] 50 | [-i ARG] [-S ARG] [-E ARG] [-I ARG] [-o ARG] 51 | [-p ARG] 52 | 53 | Create parallax scrolling background for SGDK 54 | 55 | options: 56 | -h, --help show this help message and exit 57 | -v, --verbose increase output verbosity 58 | -f ARG, --far_image_reps ARG 59 | How many times to repeat image at far side of floor 60 | -n ARG, --near_image_reps ARG 61 | How many times to repeat image at near side of floor 62 | -s ARG, --start_row ARG 63 | Which row starts the floor 64 | -e ARG, --end_row ARG 65 | Which row ends the floor 66 | -i ARG, --input_filename ARG 67 | input image filename 68 | -S ARG, --start_ceiling_row ARG 69 | Which row starts the ceiling 70 | -E ARG, --end_ceiling_row ARG 71 | Which row ends the ceiling 72 | -I ARG, --input_ceiling_filename ARG 73 | input ceiling image filename 74 | -o ARG, --output_filename ARG 75 | Output filename 76 | -p ARG, --project_directory ARG 77 | Create project directory with resource files and simple SGDK code. 78 | ``` 79 | 80 | # Examples 81 | ## Floor with two near repetitions and four far repetitions 82 | The default behavior is to take an input image named "image.png" and repeated 83 | it twice at the bottom row of the image (row 223) and four times at the top of 84 | the floor pattern (row 80). Type 85 | ```bash 86 | python3 sgdk_endless_scroll.py 87 | ``` 88 | This generates an image called `bg.png` and should output text similar to the 89 | following 90 | ```cmd 91 | Scroll width far: 80.000 92 | Scroll width near: 160.000 93 | Starting row floor: 180 94 | Ending row floor: 223 95 | * Scroll increment floor: 0.0233 96 | * Final Scroll increment floor: 1.8605 97 | Image size 480 x 224 98 | ``` 99 | ![default background](https://raw.githubusercontent.com/radioation/SGDK_Scrolling/main/EndlessScroll/bg.png) 100 | The output gives you information you can use to scroll the image. 101 | * Scroll width far 80.000 : The repeated image takes up 80 pixels at the top row of the floor. 102 | * Scroll width near 160.000 : The repeated image takes up 160 pixels at the bottom row of the floor. 103 | * Starting row floor 180 : The floor starts at row 180 in the output image 104 | * Ending row floor 223 : The floor ends at row 223 in the output image 105 | * Scroll increment floor 0.0233 : The amount each successive scroll line should increase if you're scrolling the top row by one pixel. 106 | * Final Scroll increment floor 1.8605 : The amount each floor row differs by before the scroll values needs to be reset. 107 | * Image size 480 x 224 : The size of the output image. 108 | 109 | 110 | 111 | If you want the floor rows to scroll right to left, you'd move row 80 left 112 | by one pixel, row 81 left by 1.0233 pixels, row 81 by 1.0466, pixels, and so 113 | on. 114 | ```c 115 | ++scrollStep; 116 | fix32 fStep = FIX32(1.0); 117 | for( u16 row=180; row<224; ++row ) { 118 | fscroll[row] = fix32Sub( fscroll[row], fStep ); // shift row left 119 | fStep = fix32Add( fStep, FIX32(0.0233)); // change shift by 0.0233 pixels 120 | } 121 | ``` 122 | This works up to 80 pixels for the top row. This is because the output image 123 | only has enough pixles drawn to scroll one repetition. To handle this, reset 124 | the scroll values when the top row has moved 80 pixels. 125 | ```c 126 | scrollStep = 0; 127 | memset(fscroll, 0, sizeof(fscroll)); // zero out all scroll values 128 | ``` 129 | Put together: 130 | ```c 131 | static scrollLeftLoop() { 132 | ++scrollStep; 133 | fix32 fStep = FIX32(1.0); 134 | if (scrollStep < 80) { 135 | for( u16 row=180; row<224; ++row ) { 136 | fscroll[row] = fix32Sub( fscroll[row], fStep ); 137 | fStep = fix32Add( fStep, FIX32(0.0233)); 138 | } 139 | } else { 140 | scrollStep = 0; 141 | memset(fscroll, 0, sizeof(fscroll)); 142 | } 143 | } 144 | ``` 145 | 146 | Scrolling the floor left to right is similar. The main difference is you 147 | move the rows in the opposite direction. Row 80 moves to the right by one 148 | pixel, row 81 right by 1.0233 pixels, row 81 by 1.0466, pixels, etc. 149 | ```c 150 | --scrollStep; 151 | fix32 fStep = FIX32(1.0); 152 | for (u16 row = 180; row < 224; ++row) 153 | { 154 | fscroll[row] = fix32Add(fscroll[row], fStep); 155 | fStep = fix32Add(fStep, FIX32(0.0233)); 156 | } 157 | ``` 158 | Again, this only works up to 80 pixels for the top row. Resetting the scroll 159 | lines is a bit more complex here. You have to put all of the lines at their 160 | Final scroll position. This can be computed with the 'final scroll increment' 161 | value output from the script. In this case: 162 | 163 | `* Final Scroll increment floor: 1.8605` 164 | 165 | ```c 166 | scrollStep = 80; 167 | fix32 scroll = FIX32(-80.0); // scrolled by 80. 168 | for (u16 row = 180; row < 224; ++row) 169 | { 170 | fscroll[row] = scroll; 171 | scroll = fix32Sub(scroll, FIX32(1.8605)); // decrement by final value 172 | } 173 | ``` 174 | Put together: 175 | ```c 176 | static scrollRightLoop() 177 | { 178 | --scrollStep; 179 | fix32 fStep = FIX32(1.0); 180 | if (scrollStep >= 0) 181 | { 182 | for (u16 row = 180; row < 224; ++row) 183 | { 184 | fscroll[row] = fix32Add(fscroll[row], fStep); 185 | fStep = fix32Add(fStep, FIX32(0.0233)); 186 | } 187 | } 188 | else 189 | { 190 | scrollStep = 80; 191 | fix32 scroll = FIX32(-80.0); 192 | for (u16 row = 180; row < 224; ++row) 193 | { 194 | fscroll[row] = scroll; 195 | scroll = fix32Sub(scroll, FIX32(1.8605)); 196 | } 197 | } 198 | } 199 | ``` 200 | 201 | ## Floor with four near repetitions and 6 far repetitions 202 | You can increase the number of image repetitions with the `-f` and `-n` 203 | parameters. To have six repetitions at the far side of the floor and four 204 | repetitions at the near side run these parameters: 205 | ```bash 206 | python3 sgdk_endless_scroll.py -i test_tile.png -f 6 -n 4 -o tilefloor.png 207 | ``` 208 | I also specified a different floor image with the `-i` parameter and changed the 209 | output filename with `-o`. 210 | ![Tile Floor](https://raw.githubusercontent.com/radioation/SGDK_Scrolling/main/EndlessScroll/tilefloor.png) 211 | ## Add a ceiling 212 | Adding a ceiling can be done by specifying the start (`-S`) and end (`-E`) rows for the ceiling. 213 | ```bash 214 | python3 sgdk_endless_scroll.py -i test_wood.png -f 6 -n 4 -S 8 -E 48 -o woodceil.png 215 | ``` 216 | ![Wood Ceiling](https://raw.githubusercontent.com/radioation/SGDK_Scrolling/main/EndlessScroll/woodceil.png) 217 | ## Add a ceiling with its own tile images 218 | Specifying `-I` lets you add a second image for the ceiling. 219 | ```bash 220 | python3 sgdk_endless_scroll.py -i test_wood.png -f 6 -n 4 -S 8 -E 48 -I test_tile.png -o woodfloor_tileceiling.png 221 | ``` 222 | ![Wood Floor Tile Ceiling](https://raw.githubusercontent.com/radioation/SGDK_Scrolling/main/EndlessScroll/woodfloor_tileceiling.png) 223 | 224 | ## Create an example project 225 | Specifying a folder with the `-p` parameter will create a project folder with files for SGDK. 226 | If your environment is setup correctly, the project files can be built with: 227 | ```cmd 228 | %GDK%\bin\make -f %GDK%\makefile.gen 229 | ``` 230 | 231 | 232 | 233 | 234 | 235 | -------------------------------------------------------------------------------- /EndlessScroll/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/EndlessScroll/bg.png -------------------------------------------------------------------------------- /EndlessScroll/ceiling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/EndlessScroll/ceiling.png -------------------------------------------------------------------------------- /EndlessScroll/example/res/bg/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/EndlessScroll/example/res/bg/bg.png -------------------------------------------------------------------------------- /EndlessScroll/example/res/resources.res: -------------------------------------------------------------------------------- 1 | image plane_b "bg/bg.png" 0 2 | PALETTE plane_b_pal "bg/bg.png" 3 | -------------------------------------------------------------------------------- /EndlessScroll/example/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | 4 | s16 hScrollB[224]; 5 | fix32 fscroll[224]; 6 | s16 scrollStep = 0; 7 | 8 | static scrollLeftLoop() 9 | { 10 | ++scrollStep; 11 | fix32 fStep = FIX32(1.0); 12 | if (scrollStep < 80) 13 | { 14 | for (u16 row = 180; row < 224; ++row) 15 | { 16 | fscroll[row] = fix32Sub(fscroll[row], fStep); 17 | fStep = fix32Add(fStep, FIX32(0.0233)); 18 | } 19 | } 20 | else 21 | { 22 | scrollStep = 0; 23 | memset(fscroll, 0, sizeof(fscroll)); 24 | } 25 | } 26 | 27 | static scrollRightLoop() 28 | { 29 | --scrollStep; 30 | fix32 fStep = FIX32(1.0); 31 | if (scrollStep >= 0) 32 | { 33 | for (u16 row = 180; row < 224; ++row) 34 | { 35 | fscroll[row] = fix32Add(fscroll[row], fStep); 36 | fStep = fix32Add(fStep, FIX32(0.0233)); 37 | } 38 | } 39 | else 40 | { 41 | scrollStep = 80; 42 | fix32 scroll = FIX32(-80.0); 43 | for (u16 row = 180; row < 224; ++row) 44 | { 45 | fscroll[row] = scroll; 46 | scroll = fix32Sub(scroll, FIX32(1.8605)); 47 | } 48 | } 49 | } 50 | 51 | int main(bool hard) 52 | { 53 | memset(hScrollB, 0, sizeof(hScrollB)); 54 | memset(fscroll, 0, sizeof(fscroll)); 55 | VDP_setScreenWidth320(); 56 | 57 | PAL_setPalette(PAL0, plane_b_pal.data, CPU); 58 | PAL_setColor(0, 0x0000); 59 | // set scrolling modes to support fake rotation 60 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_PLANE); 61 | 62 | // get initial tile position in VRAM 63 | int ind = TILE_USER_INDEX; 64 | int indexB = ind; 65 | VDP_loadTileSet(plane_b.tileset, indexB, CPU); 66 | VDP_drawImageEx(BG_B, &plane_b, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, indexB), 0, 0, FALSE, TRUE); 67 | 68 | while (TRUE) 69 | { 70 | scrollRightLoop(); 71 | //scrollLeftLoop(); 72 | for (int i = 0; i < 224; i++) // Not very efficient. 73 | { 74 | hScrollB[i] = fix32ToInt(fscroll[i]); 75 | } 76 | VDP_setHorizontalScrollLine(BG_B, 0, hScrollB, 224, DMA); 77 | SYS_doVBlankProcess(); 78 | } 79 | } -------------------------------------------------------------------------------- /EndlessScroll/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/EndlessScroll/image.png -------------------------------------------------------------------------------- /EndlessScroll/main.c.jinja: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | 4 | s16 hScrollB[224]; 5 | fix32 fscroll[224]; 6 | s16 scrollStep = 0; 7 | 8 | static void scrollLeft() { 9 | ++scrollStep; 10 | if (scrollStep < {{ far_width|int }} ) { 11 | {% for scrollvals in scroll_left_list %} 12 | fscroll[{{scrollvals[0]}}] = fscroll[{{scrollvals[0]}}] - FIX32( {{'%0.3f' % scrollvals[1]|float }} ); 13 | {%- endfor %} 14 | } else { 15 | scrollStep = 0; 16 | memset(fscroll, 0, sizeof(fscroll)); 17 | } 18 | } 19 | 20 | static void scrollRight() { 21 | --scrollStep; 22 | if (scrollStep >= 0) { 23 | {% for scrollvals in scroll_right_list %} 24 | fscroll[{{scrollvals[0]}}] = fscroll[{{scrollvals[0]}}] + FIX32( {{'%0.3f' % scrollvals[1]|float }} ); 25 | {%- endfor %} 26 | 27 | } else { 28 | scrollStep = {{ far_width|int }}; 29 | {% for scrollvals in scroll_right_reset_list %} 30 | fscroll[{{scrollvals[0]}}] = FIX32({{ '%0.3f' % scrollvals[1]|float }}); 31 | {%- endfor %} 32 | } 33 | } 34 | 35 | int main(bool hard) 36 | { 37 | memset(hScrollB, 0, sizeof(hScrollB)); 38 | memset(fscroll, 0, sizeof(fscroll)); 39 | VDP_setScreenWidth320(); 40 | 41 | PAL_setPalette(PAL0, {{ bg_name }}_pal.data, CPU); 42 | PAL_setColor(0, 0x0000); 43 | // set scrolling modes to support line scrolling. 44 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_PLANE); 45 | 46 | // get initial tile position in VRAM and load image 47 | int ind = TILE_USER_INDEX; 48 | int indexB = ind; 49 | VDP_loadTileSet({{ bg_name }}.tileset, indexB, CPU); 50 | VDP_drawImageEx(BG_B, &{{ bg_name }}, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, indexB), 0, 0, FALSE, TRUE); 51 | 52 | while (TRUE) 53 | { 54 | scrollLeft(); 55 | //scrollRight(); 56 | for (int i = 0; i < 224; i++) // Not very efficient, but works for a demo 57 | { 58 | hScrollB[i] = fix32ToInt(fscroll[i]); 59 | } 60 | VDP_setHorizontalScrollLine(BG_B, 0, hScrollB, 224, DMA); 61 | SYS_doVBlankProcess(); 62 | } 63 | } 64 | 65 | 66 | -------------------------------------------------------------------------------- /EndlessScroll/main_wide.c.jinja: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | 4 | s16 hScrollB[224]; 5 | fix32 fscroll[224]; 6 | s16 scrollStep = 0; 7 | s16 offset = 0; 8 | s16 imageOffset = 0; 9 | s16 ind = 0; 10 | static void scrollLeft() { 11 | 12 | ++scrollStep; 13 | 14 | if (scrollStep < {{ far_width|int }} ) { 15 | {% for scrollvals in scroll_left_list %} 16 | fscroll[{{scrollvals[0]}}] = fscroll[{{scrollvals[0]}}] - FIX32( {{'%0.3f' % scrollvals[1]|float }} ); 17 | {%- endfor %} 18 | } else { 19 | scrollStep = 0; 20 | offset = 0; 21 | imageOffset = 0; 22 | VDP_setTileMapEx(BG_B, {{ bg_name }}.tilemap, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, ind), 23 | 0, // Plane X destination 24 | 0, // plane Y destination 25 | 0, // Region X start position 26 | 0, // Region Y start position 27 | 32, // width (went with 64 becasue default width is 64. Viewable screen is 40) 28 | 28, // height 29 | DMA_QUEUE); 30 | memset(fscroll, 0, sizeof(fscroll)); 31 | } 32 | } 33 | 34 | 35 | int main(bool hard) 36 | { 37 | memset(hScrollB, 0, sizeof(hScrollB)); 38 | memset(fscroll, 0, sizeof(fscroll)); 39 | VDP_setScreenWidth320(); 40 | 41 | PAL_setPalette(PAL0, {{ bg_name }}_pal.data, CPU); 42 | PAL_setColor(0, 0x0000); 43 | 44 | // set scrolling modes to support line scrolling. 45 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_PLANE); 46 | 47 | // get initial tile position in VRAM and load image 48 | ind = TILE_USER_INDEX; 49 | int indexB = ind; 50 | VDP_loadTileSet(bg.tileset, indexB, CPU); 51 | 52 | //VDP_drawImageEx(BG_B, &bg, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, indexB), 0, 0, FALSE, TRUE); 53 | 54 | // put out the image 55 | VDP_setTileMapEx(BG_B, {{ bg_name }}.tilemap, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, ind), 56 | 0, // Plane X destination 57 | 0, // plane Y destination 58 | 0, // Region X start position 59 | 0, // Region Y start position 60 | 64, // width (went with 64 becasue default width is 64. Viewable screen is 40) 61 | 28, // height 62 | CPU); 63 | 64 | 65 | u16 srcCols = {{ image_width / 8 }}; 66 | while (TRUE) 67 | { 68 | scrollLeft(); 69 | // tile in memory 70 | offset += 1; 71 | if (offset > 511) { 72 | offset = 0; 73 | } 74 | 75 | // tile from image 76 | imageOffset += 1; 77 | if (imageOffset > {{ image_width - 1 }}) { 78 | imageOffset = 0; // bg image is 640 pixels wide 79 | } 80 | 81 | if (offset % 8 == 0 && scrollStep !=0) 82 | { 83 | s16 dstCol = (offset + 504) / 8; 84 | if (dstCol > 63) 85 | { 86 | dstCol -= 64; // wrap to zero 87 | } 88 | 89 | s16 srcCol = (imageOffset + 508) / 8; 90 | if (srcCol > srcCols - 1) 91 | { 92 | srcCol -= srcCols; // wrap to zero 93 | } 94 | 95 | VDP_setTileMapEx(BG_B, {{ bg_name }}.tilemap, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, ind), 96 | dstCol, // Plane X destination 97 | 0, // plane Y destination 98 | srcCol, // Region X start position 99 | 0, // Region Y start position 100 | 1, // width 101 | 28, // height 102 | DMA_QUEUE); 103 | } 104 | 105 | 106 | //scrollRight(); 107 | for (int i = 0; i < 224; i++) // Not very efficient, but works for a demo 108 | { 109 | hScrollB[i] = fix32ToInt(fscroll[i]); 110 | } 111 | VDP_setHorizontalScrollLine(BG_B, 0, hScrollB, 224, DMA_QUEUE); 112 | SYS_doVBlankProcess(); 113 | } 114 | } 115 | 116 | -------------------------------------------------------------------------------- /EndlessScroll/python_win_install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/EndlessScroll/python_win_install.png -------------------------------------------------------------------------------- /EndlessScroll/resources.res.jinja: -------------------------------------------------------------------------------- 1 | IMAGE {{ bg_name }} "bg/{{ bg_name }}.png" NONE 2 | 3 | PALETTE {{ bg_name }}_pal "bg/{{ bg_name }}.png" 4 | -------------------------------------------------------------------------------- /EndlessScroll/sgdk_endless_scroll.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os, argparse, logging 4 | import numpy as np 5 | import math 6 | from PIL import Image, ImageDraw 7 | import cv2 8 | import shutil 9 | from jinja2 import Template 10 | from pathlib import Path 11 | 12 | def makeProjectFiles( destDir, imageFilename, endRow, startRow, nearPolyWidth, farPolyWidth, endCeilingRow, startCeilingRow, imageWidth ): 13 | # make resource dir and rescomp file 14 | srcFolder = destDir + "/src" 15 | if not os.path.exists(srcFolder): 16 | os.makedirs(srcFolder) 17 | resFolder = destDir + "/res" 18 | if not os.path.exists(resFolder): 19 | os.makedirs(resFolder) 20 | bgFolder = resFolder + "/bg" 21 | if not os.path.exists(bgFolder): 22 | os.makedirs(bgFolder) 23 | 24 | # copy out image to backtround folder 25 | shutil.copy( imageFilename, bgFolder ) 26 | 27 | # Make resource file 28 | fname = Path( imageFilename ).stem 29 | # Make resources file from template 30 | with open('resources.res.jinja') as resFile: 31 | resTemp = Template( resFile.read() ) 32 | with open( resFolder + "/resources.res", 'w') as outRes: 33 | outRes.write( resTemp.render( 34 | bg_name = fname 35 | )) 36 | 37 | # Make main.c file from template 38 | scrollLeftList=[] 39 | scrollCeilingRows = endCeilingRow - startCeilingRow 40 | scrollCeilingRatio = nearPolyWidth / farPolyWidth 41 | scrollCeilingRowStep = 0 42 | scrollCeilingIncrement = 1.0 43 | 44 | if startCeilingRow > -1 and endCeilingRow > startCeilingRow : 45 | scrollCeilingRowStep = ( scrollCeilingRatio - 1.0 ) / scrollCeilingRows 46 | for r in range( endCeilingRow, startCeilingRow - 1, -1): 47 | #srcfile.write( " fscroll[%d] = fix32Sub( fscroll[%d], FIX32(%.3f));\n" % ( r, r, scrollCeilingIncrement ) ) 48 | scrollLeftList.append( (r, scrollCeilingIncrement ) ) 49 | scrollCeilingIncrement += scrollCeilingRowStep 50 | 51 | 52 | scrollRows = endRow - startRow 53 | scrollRatio = nearPolyWidth / farPolyWidth 54 | scrollRowStep = ( scrollRatio - 1.0 ) / scrollRows 55 | scrollIncrement = 1.0; 56 | for r in range( startRow, endRow + 1, 1): 57 | ##srcfile.write( " fscroll[%d] = fix32Sub( fscroll[%d], FIX32(%.3f));\n" % ( r, r, scrollIncrement ) ) 58 | scrollLeftList.append( (r, scrollIncrement ) ) 59 | scrollIncrement += scrollRowStep 60 | 61 | 62 | scrollRightList = [] 63 | if startCeilingRow > -1 and endCeilingRow > startCeilingRow : 64 | scrollCeilingIncrement = 1.0 65 | scrollCeilingRowStep = ( scrollCeilingRatio - 1.0 ) / scrollCeilingRows 66 | for r in range( endCeilingRow, startCeilingRow - 1, -1): 67 | #srcfile.write( " fscroll[%d] = fix32Add( fscroll[%d], FIX32(%.3f));\n" % ( r, r, scrollCeilingIncrement ) ) 68 | scrollRightList.append( (r, scrollCeilingIncrement ) ) 69 | scrollCeilingIncrement += scrollCeilingRowStep 70 | scrollIncrement = 1.0; 71 | for r in range( startRow, endRow + 1, 1): 72 | #srcfile.write( " fscroll[%d] = fix32Add( fscroll[%d], FIX32(%.3f));\n" % ( r, r, scrollIncrement ) ) 73 | scrollRightList.append( (r, scrollIncrement ) ) 74 | scrollIncrement += scrollRowStep 75 | 76 | scrollRightResetList = [] 77 | 78 | if startCeilingRow > -1 and endCeilingRow > startCeilingRow : 79 | scroll = - int( farPolyWidth) 80 | scrollStep = (nearPolyWidth - farPolyWidth) / scrollCeilingRows 81 | for r in range( endCeilingRow, startCeilingRow , -1): 82 | #srcfile.write( " fscroll[%d] = FIX32(%.3f);\n" % ( r, scroll ) ) 83 | scrollRightResetList.append( (r, scroll ) ) 84 | scroll -= scrollStep 85 | 86 | scroll = - int( farPolyWidth) 87 | finalScrollStep = (nearPolyWidth - farPolyWidth) / scrollRows 88 | for r in range( startRow, endRow + 1, 1): 89 | #srcfile.write( " fscroll[%d] = FIX32(%.3f);\n" % ( r, scroll ) ) 90 | scrollRightResetList.append( (r, scroll ) ) 91 | scroll -= finalScrollStep 92 | 93 | if imageWidth <= 512: 94 | with open('main.c.jinja') as srcFile: 95 | srcTemp = Template( srcFile.read() ) 96 | with open( srcFolder + "/main.c", 'w') as outSrc: 97 | outSrc.write( srcTemp.render( 98 | bg_name = fname, 99 | far_width = farPolyWidth, 100 | scroll_left_list = scrollLeftList, 101 | scroll_right_list = scrollRightList, 102 | scroll_right_reset_list = scrollRightResetList, 103 | 104 | )) 105 | else: 106 | with open('main_wide.c.jinja') as srcFile: 107 | srcTemp = Template( srcFile.read() ) 108 | with open( srcFolder + "/main.c", 'w') as outSrc: 109 | outSrc.write( srcTemp.render( 110 | bg_name = fname, 111 | far_width = farPolyWidth, 112 | scroll_left_list = scrollLeftList, 113 | image_width = imageWidth, 114 | )) 115 | 116 | 117 | def main(args, loglevel): 118 | logging.basicConfig(format="%(levelname)s: %(message)s", level=loglevel) 119 | startRow = args.start_row 120 | endRow = args.end_row 121 | if startRow == endRow: 122 | print("Start and end row of floor must differ") 123 | return 124 | elif startRow > endRow: 125 | temp = endRow 126 | endRow = startRow 127 | startRow = temp 128 | 129 | 130 | startCeilingRow = args.start_ceiling_row 131 | endCeilingRow = args.end_ceiling_row 132 | if startCeilingRow == endCeilingRow and startCeilingRow > 0: 133 | print("Start and end row of ceiling must differ") 134 | return 135 | elif startCeilingRow > endCeilingRow: 136 | temp = endCeilingRow 137 | endCeilingRow = startCeilingRow 138 | startCeilingRow = temp 139 | 140 | farImageReps = args.far_image_reps 141 | nearImageReps = args.near_image_reps 142 | if farImageReps <= nearImageReps: 143 | print("Far image reps must be larger than near image reps") 144 | return 145 | 146 | 147 | imageFilename = args.input_filename 148 | 149 | imageCeilingFilename = args.input_ceiling_filename 150 | 151 | outputFilename = args.output_filename 152 | 153 | projectDir = args.project_directory 154 | 155 | # 320 x 224 156 | COLS = 320 157 | rows = 224 158 | 159 | farPolyWidth = COLS / farImageReps 160 | nearPolyWidth = COLS / nearImageReps 161 | 162 | logging.info("Image reps far: %d", farImageReps) 163 | logging.info("Image reps near: %d", nearImageReps) 164 | # always put out image repetition 165 | print("Scroll width far: %.3f" % farPolyWidth) 166 | print("Scroll width near: %.3f" % nearPolyWidth) 167 | print("Starting row floor: %d" % startRow) 168 | print("Ending row floor: %d" % endRow) 169 | scrollRows = endRow - startRow 170 | scrollRatio = nearPolyWidth / farPolyWidth 171 | scrollRowStep = ( scrollRatio - 1.0 ) / scrollRows 172 | print("* Scroll increment floor: %.4f" % scrollRowStep) 173 | finalScrollStep = (nearPolyWidth - farPolyWidth) / scrollRows 174 | print("* Final Scroll increment floor: %.4f" % finalScrollStep) 175 | 176 | 177 | if startCeilingRow > -1 and endCeilingRow > startCeilingRow : 178 | logging.info("Starting row ceiling: %d", startCeilingRow) 179 | logging.info("Ending row ceiling: %d", endCeilingRow) 180 | scrollCeilingRows = endCeilingRow - startCeilingRow 181 | scrollCeilingRatio = nearPolyWidth / farPolyWidth 182 | scrollCeilingRowStep = ( scrollCeilingRatio - 1.0 ) / scrollCeilingRows 183 | print("* Scroll increment ceiling: %.4f" % scrollCeilingRowStep) 184 | 185 | 186 | bottomTotalWidth = nearPolyWidth * farImageReps 187 | offset = farImageReps % 2 != nearImageReps % 2 188 | outputCols = int(COLS + nearPolyWidth) 189 | # must be multiple of 8 for tile breakdown 190 | if outputCols % 8 > 0: 191 | outputCols = (int( outputCols/8) +1) * 8 192 | 193 | print("Image size %d x %d" % ( outputCols,rows ) ) 194 | 195 | with Image.open( imageFilename ) as im: 196 | inputImg = im.convert('RGB') 197 | inputWidth, inputHeight = im.size 198 | inputCv = np.array(inputImg) 199 | 200 | pal = im.getpalette() 201 | # get source points 202 | srcTopLeft = ( 0, 0 ) 203 | srcTopRight = ( inputWidth-1, 0 ) 204 | srcBottomLeft = ( 0, inputHeight-1 ) 205 | srcBottomRight = ( inputWidth-1, inputHeight-1 ) 206 | srcPts = np.array( [ srcBottomLeft, srcBottomRight, srcTopRight, srcTopLeft] ) 207 | logging.info("srcPts:"); 208 | logging.info(srcPts); 209 | 210 | # work image 211 | tmpImg = Image.new('RGB', (outputCols, rows)) 212 | tmpCv = np.array(tmpImg) 213 | bottomLeftStart = COLS / 2 - bottomTotalWidth / 2 214 | for rep in range( 0, farImageReps +1, 1 ): 215 | # detination points 216 | dstTopLeft = ( rep * farPolyWidth, startRow ) 217 | dstTopRight = ( rep * farPolyWidth + farPolyWidth-0.5, startRow ) 218 | dstBottomLeft = ( bottomLeftStart + rep * nearPolyWidth, endRow ) 219 | dstBottomRight = ( bottomLeftStart + rep * nearPolyWidth + nearPolyWidth-0.5, endRow ) 220 | dstPts = np.array( [ dstBottomLeft, dstBottomRight, dstTopRight, dstTopLeft] ) 221 | dstPoly = np.array( [ dstBottomLeft, dstBottomRight, dstTopRight, dstTopLeft], dtype=np.int32 ) 222 | logging.info("dstPts:"); 223 | logging.info(dstPts); 224 | 225 | # Get Perspective Transform Algorithm 226 | srcPtsList = np.float32( srcPts.tolist() ) 227 | dstPtsList = np.float32( dstPts.tolist() ) 228 | xfrmMatrix = cv2.getPerspectiveTransform(srcPtsList, dstPtsList) 229 | 230 | # warp it 231 | image_size = (tmpCv.shape[1], tmpCv.shape[0]) 232 | warpCv = cv2.warpPerspective(inputCv, xfrmMatrix, dsize=image_size) 233 | warpImg = Image.fromarray( warpCv ) 234 | if args.output_warped_images: 235 | warpImg.save( "warped_floor_%d.png" %(rep) ) 236 | 237 | ## create a copy mask 238 | maskCv = np.zeros_like(tmpCv) 239 | maskCv = cv2.fillPoly(maskCv, pts = [dstPoly], color = (255, 255, 255) ) 240 | maskCv = maskCv.all(axis=2) 241 | # copy warped image 242 | tmpCv[maskCv, :] = warpCv[maskCv, :] 243 | 244 | # check if ceilng was set. 245 | if startCeilingRow >= 0 and endCeilingRow > 0: 246 | ceilFilename = imageCeilingFilename if len(imageCeilingFilename) > 0 else imageFilename 247 | with Image.open( ceilFilename ) as ceil: 248 | inputCeilingImg = ceil.convert('RGB') 249 | inputWidth, inputHeight = ceil.size 250 | inputCeilingCv = np.array(inputCeilingImg) 251 | # assume old pal 252 | # get source points 253 | srcTopLeft = ( 0, 0 ) 254 | srcTopRight = ( inputWidth-1, 0 ) 255 | srcBottomLeft = ( 0, inputHeight-1 ) 256 | srcBottomRight = ( inputWidth-1, inputHeight-1 ) 257 | srcPts = np.array( [ srcBottomLeft, srcBottomRight, srcTopRight, srcTopLeft] ) 258 | logging.info("srcPts:"); 259 | logging.info(srcPts); 260 | topLeftStart = COLS / 2 - bottomTotalWidth / 2 261 | for rep in range( 0, farImageReps +1, 1 ): 262 | # detination points 263 | dstTopLeft = ( topLeftStart + rep * nearPolyWidth, startCeilingRow ) 264 | dstTopRight = ( topLeftStart + rep * nearPolyWidth + nearPolyWidth, startCeilingRow ) 265 | dstBottomLeft = ( rep * farPolyWidth, endCeilingRow ) 266 | dstBottomRight = ( rep * farPolyWidth + farPolyWidth, endCeilingRow ) 267 | dstPts = np.array( [ dstBottomLeft, dstBottomRight, dstTopRight, dstTopLeft] ) 268 | logging.info("dstPts:"); 269 | logging.info(dstPts); 270 | dstPoly = np.array( [ dstBottomLeft, dstBottomRight, dstTopRight, dstTopLeft], dtype=np.int32 ) 271 | # Get Perspective Transform Algorithm 272 | srcPtsList = np.float32( srcPts.tolist() ) 273 | dstPtsList = np.float32( dstPts.tolist() ) 274 | xfrmMatrix = cv2.getPerspectiveTransform(srcPtsList, dstPtsList) 275 | 276 | # warp it 277 | image_size = (tmpCv.shape[1], tmpCv.shape[0]) 278 | warpCv = cv2.warpPerspective(inputCeilingCv, xfrmMatrix, dsize=image_size) 279 | warpImg = Image.fromarray( warpCv ) 280 | if args.output_warped_images: 281 | warpImg.save( "warped_ceil_%d.png" %(rep) ) 282 | 283 | ## create a copy mask 284 | maskCv = np.zeros_like(tmpCv) 285 | maskCv = cv2.fillPoly(maskCv, pts = [dstPoly], color = (255, 255, 255) ) 286 | maskCv = maskCv.all(axis=2) 287 | # copy warped image 288 | tmpCv[maskCv, :] = warpCv[maskCv, :] 289 | 290 | 291 | # convert backto PIL 292 | maskImg = Image.fromarray( tmpCv ) 293 | ImageDraw.floodfill( maskImg, ( outputCols-1, rows/2), ( pal[0], pal[1], pal[2]) ) 294 | ##maskImg.save( "mask.png") 295 | outImg = maskImg.quantize( palette = im ) 296 | #outImg = Image.new('P', (outputCols, rows)) 297 | #outImg.putpalette(pal) 298 | outImg.save( outputFilename ) 299 | 300 | if len(projectDir) > 0 : 301 | makeProjectFiles( projectDir, outputFilename, endRow, startRow, nearPolyWidth, farPolyWidth, endCeilingRow, startCeilingRow, outputCols ) 302 | 303 | # the program. 304 | if __name__ == '__main__': 305 | parser = argparse.ArgumentParser( 306 | description = "Create endless scrolling background for SGDK", 307 | epilog = "As an alternative to the commandline, params can be placed in a file, one per line, and specified on the commandline like '%(prog)s @params.conf'.", 308 | fromfile_prefix_chars = '@' ) 309 | # parameter list 310 | parser.add_argument( 311 | "-v", 312 | "--verbose", 313 | help="increase output verbosity", 314 | action="store_true") 315 | 316 | parser.add_argument( "-f", 317 | "--far_image_reps", 318 | default = 4, 319 | type=int, 320 | help = "How many times to repeat image at far side of the floor/ceiling", 321 | metavar = "ARG") 322 | 323 | parser.add_argument( "-n", 324 | "--near_image_reps", 325 | default = 2, 326 | type=int, 327 | help = "How many times to repeat image at near side of the floor/ceiling", 328 | metavar = "ARG") 329 | 330 | parser.add_argument( "-s", 331 | "--start_row", 332 | default = 180, 333 | type=int, 334 | help = "Which row starts the floor", 335 | metavar = "ARG") 336 | 337 | parser.add_argument( "-e", 338 | "--end_row", 339 | default = 223, 340 | type=int, 341 | help = "Which row ends the floor", 342 | metavar = "ARG") 343 | 344 | 345 | 346 | parser.add_argument( "-i", 347 | "--input_filename", 348 | default = 'image.png', 349 | help = "input image filename", 350 | metavar = "ARG") 351 | 352 | parser.add_argument( "-S", 353 | "--start_ceiling_row", 354 | default = -1, 355 | type=int, 356 | help = "Which row starts the ceiling", 357 | metavar = "ARG") 358 | 359 | parser.add_argument( "-E", 360 | "--end_ceiling_row", 361 | default = -1, 362 | type=int, 363 | help = "Which row ends the ceiling", 364 | metavar = "ARG") 365 | 366 | parser.add_argument( "-I", 367 | "--input_ceiling_filename", 368 | default = '', 369 | help = "input ceiling image filename", 370 | metavar = "ARG") 371 | 372 | parser.add_argument( "-o", 373 | "--output_filename", 374 | default = 'bg.png', 375 | help = "Output filename", 376 | metavar = "ARG") 377 | 378 | parser.add_argument( "-p", 379 | "--project_directory", 380 | default = '', 381 | help = "Create project directory with resource files and simple SGDK code.", 382 | metavar = "ARG") 383 | 384 | parser.add_argument( 385 | "-w", 386 | "--output_warped_images", 387 | help="Output warped images used to create final image.", 388 | action="store_true") 389 | 390 | args = parser.parse_args() 391 | 392 | # Setup logging 393 | if args.verbose: 394 | loglevel = logging.INFO 395 | else: 396 | loglevel = logging.WARNING 397 | 398 | main(args, loglevel) 399 | -------------------------------------------------------------------------------- /EndlessScroll/test_tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/EndlessScroll/test_tile.png -------------------------------------------------------------------------------- /EndlessScroll/test_wood.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/EndlessScroll/test_wood.png -------------------------------------------------------------------------------- /EndlessScroll/tilefloor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/EndlessScroll/tilefloor.png -------------------------------------------------------------------------------- /EndlessScroll/warp_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/EndlessScroll/warp_0.png -------------------------------------------------------------------------------- /EndlessScroll/warp_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/EndlessScroll/warp_1.png -------------------------------------------------------------------------------- /EndlessScroll/warp_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/EndlessScroll/warp_2.png -------------------------------------------------------------------------------- /EndlessScroll/warp_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/EndlessScroll/warp_3.png -------------------------------------------------------------------------------- /EndlessScroll/warp_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/EndlessScroll/warp_4.png -------------------------------------------------------------------------------- /EndlessScroll/warp_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/EndlessScroll/warp_5.png -------------------------------------------------------------------------------- /EndlessScroll/warp_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/EndlessScroll/warp_6.png -------------------------------------------------------------------------------- /EndlessScroll/woodceil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/EndlessScroll/woodceil.png -------------------------------------------------------------------------------- /EndlessScroll/woodfloor_tileceiling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/EndlessScroll/woodfloor_tileceiling.png -------------------------------------------------------------------------------- /HScroll/res/planes/madmarco_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/HScroll/res/planes/madmarco_bg.png -------------------------------------------------------------------------------- /HScroll/res/resources.res: -------------------------------------------------------------------------------- 1 | PALETTE bg_palette "planes/madmarco_bg.png" NONE 2 | IMAGE bg_image "planes/madmarco_bg.png" NONE 3 | -------------------------------------------------------------------------------- /HScroll/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | 4 | 5 | s16 offset = 0; 6 | s16 imageOffset = 0; 7 | 8 | int main( bool hard ) { 9 | 10 | // load the background palette 11 | PAL_setPalette( PAL1, bg_palette.data, DMA); 12 | 13 | // set scrolling mode. HSCROLL_PLANE Affects the WHOLE plane. 14 | VDP_setScrollingMode( HSCROLL_PLANE, VSCROLL_PLANE); 15 | 16 | // get our position for tiles. 17 | int ind = TILE_USER_INDEX; 18 | 19 | // Load the plane tiles into VRAM at our position 20 | VDP_loadTileSet( bg_image.tileset, ind, DMA ); 21 | 22 | // put out the image 23 | VDP_setTileMapEx(BG_A, bg_image.tilemap, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, ind), 24 | 0, // Plane X destination 25 | 0, // plane Y destination 26 | 0, // Region X start position 27 | 0, // Region Y start position 28 | 64, // width (went with 64 becasue default width is 64. Viewable screen is 40) 29 | 28, // height 30 | CPU); 31 | 32 | while (TRUE) 33 | { 34 | // tile in memory 35 | offset += 1; 36 | if (offset > 511) 37 | offset = 0; 38 | 39 | // tile from image 40 | imageOffset += 1; 41 | if (imageOffset > 1279) 42 | imageOffset = 0; // bg image is 1280 pixels wide 43 | 44 | if (offset % 8 == 0) 45 | { 46 | s16 dstCol = (offset + 504) / 8; 47 | if (dstCol > 63) 48 | { 49 | dstCol -= 64; // wrap to zero 50 | } 51 | 52 | s16 srcCol = (imageOffset + 512) / 8; 53 | if (srcCol > 159) // 160 8-pixel columns in 1280 pixel width image 54 | { 55 | srcCol -= 160; // wrap to zero 56 | } 57 | 58 | KLog_S2("dstCol: ", dstCol, "srcCol: ", srcCol); 59 | VDP_setTileMapEx(BG_A, bg_image.tilemap, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, ind), 60 | dstCol, // Plane X destination 61 | 0, // plane Y destination 62 | srcCol, // Region X start position 63 | 0, // Region Y start position 64 | 1, // width 65 | 28, // height 66 | CPU); 67 | } 68 | 69 | VDP_setHorizontalScroll(BG_A, -offset); 70 | 71 | // let SGDK do its thing 72 | SYS_doVBlankProcess(); 73 | } 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /HScrollDec/res/planes/madmarco_bgb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/HScrollDec/res/planes/madmarco_bgb.png -------------------------------------------------------------------------------- /HScrollDec/res/planes/numbered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/HScrollDec/res/planes/numbered.png -------------------------------------------------------------------------------- /HScrollDec/res/resources.res: -------------------------------------------------------------------------------- 1 | PALETTE bg_palette_a "planes/numbered.png" 2 | IMAGE bg_image_a "planes/numbered.png" NONE 3 | IMAGE bg_image_b "planes/madmarco_bgb.png" NONE 4 | -------------------------------------------------------------------------------- /HScrollDec/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | 4 | // default SGDK width is 512 pixels (64 tiles) 5 | #define PLANE_MAX_PIXEL 512 6 | #define PLANE_MAX_TILE 64 7 | 8 | // Images in this project are 1280 pixels wide ( tiles) 9 | #define IMAGE_MAX_PIXEL 1280 10 | #define IMAGE_MAX_TILE 160 11 | 12 | fix32 offsetA = FIX32(0); 13 | fix32 imageOffsetA = FIX32(0); 14 | u16 lastSrcColA = -1; 15 | u16 lastDstColA = -1; 16 | 17 | fix32 offsetB = FIX32(0); 18 | fix32 imageOffsetB = FIX32(0); 19 | u16 lastSrcColB = -1; 20 | u16 lastDstColB = -1; 21 | 22 | s16 scrollPlane(VDPPlane plane, const TileMap *tilemap, int index, fix32 speed, fix32 *planeOffset, fix32 *imageOffset, u16 *lastSrcCol, u16 *lastDstCol) 23 | { 24 | // Set the scrolling position 25 | *planeOffset = *planeOffset + speed; 26 | if (*planeOffset >= FIX32(PLANE_MAX_PIXEL)) 27 | *planeOffset = FIX32(0); // plane in memory is 512 pixels wide 28 | *imageOffset = *imageOffset + speed; 29 | if (*imageOffset >= FIX32(IMAGE_MAX_PIXEL)) 30 | *imageOffset = FIX32(0); // bg image is 1280 pixels wide 31 | s16 sPlaneOffset = fix32ToInt(*planeOffset); 32 | // check if we need a new tile 33 | if (sPlaneOffset % 8 == 0) 34 | { 35 | // get destination column (in tiles) 36 | s16 dstCol = (sPlaneOffset + PLANE_MAX_PIXEL - 8) / 8; 37 | if (dstCol >= PLANE_MAX_TILE) 38 | { 39 | dstCol -= PLANE_MAX_TILE; // wrap around to the start of the plane 40 | } 41 | 42 | // get source column (in tiles) 43 | s16 sImageOffset = fix32ToInt(*imageOffset); 44 | s16 srcCol = (sImageOffset + PLANE_MAX_PIXEL - 8) / 8; 45 | if (srcCol >= IMAGE_MAX_TILE) 46 | { 47 | srcCol -= IMAGE_MAX_TILE; // wrap around to the start of the image 48 | } 49 | KLog_S4("dstCol: ", dstCol, " lastDisCol: ", *lastDstCol, " srcCol: ", srcCol, " lastSrcCol: ", *lastSrcCol); 50 | // if the current destination column is smaller n 51 | // the last one, the region is *notnuous 52 | // Two or more tile moves may be required. 53 | if (dstCol != *lastDstCol) 54 | { 55 | s16 width = dstCol - *lastDstCol; 56 | if (width < 0) 57 | { 58 | width += PLANE_MAX_TILE; 59 | } 60 | // just loop for now ( probably inefficient if there are a lot of columns to copy) 61 | s16 tmpDst = *lastDstCol + 1; 62 | s16 tmpSrc = *lastSrcCol + 1; 63 | for (s16 i = 0; i < width; ++i) 64 | { 65 | if (tmpDst >= PLANE_MAX_TILE) 66 | { 67 | tmpDst = 0; 68 | } 69 | if (tmpSrc >= IMAGE_MAX_TILE) 70 | { 71 | tmpSrc = 0; 72 | } 73 | KLog_S3(" C dst: ", tmpDst, " src: ", tmpSrc, " width ", 1); 74 | VDP_setTileMapEx(plane, tilemap, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, index), 75 | tmpDst, // Plane X destination 76 | 0, // plane Y destination 77 | tmpSrc, // Region X start position 78 | 0, // Region Y start position 79 | 1, // width ( will this wrap? ) 80 | 28, // height 81 | CPU); 82 | tmpDst++; 83 | tmpSrc++; 84 | } 85 | *lastDstCol = tmpDst - 1; 86 | *lastSrcCol = tmpSrc - 1; 87 | KLog_S2(" lastDstCol: ", *lastDstCol, " lastSrcCol: ", *lastSrcCol ); 88 | } 89 | } 90 | return sPlaneOffset; 91 | } 92 | 93 | int main(bool hard) 94 | { 95 | 96 | PAL_setPalette(PAL0, bg_palette_a.data, CPU); 97 | 98 | // set scrolling mode. Affects the WHOLE plane 99 | VDP_setScrollingMode(HSCROLL_PLANE, VSCROLL_PLANE); 100 | 101 | // get our position in VRAM. 102 | int ind = TILE_USER_INDEX; 103 | int indexA = ind; 104 | // Load the plane tiles into VRAM 105 | VDP_loadTileSet(bg_image_a.tileset, ind, DMA); 106 | int indexB = ind + bg_image_a.tileset->numTile; // new 107 | VDP_loadTileSet(bg_image_b.tileset, indexB, DMA); 108 | 109 | // put out the image 110 | VDP_setTileMapEx(BG_A, bg_image_a.tilemap, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, indexA), 111 | 0, // Plane X destination 112 | 0, // plane Y destination 113 | 0, // Region X start position 114 | 0, // Region Y start position 115 | PLANE_MAX_TILE, // width (went with 64 becasue default width is 64. Viewable screen is 40) 116 | 28, // height 117 | CPU); 118 | lastDstColA = PLANE_MAX_TILE - 1; 119 | lastSrcColA = PLANE_MAX_TILE - 1; 120 | VDP_setTileMapEx(BG_B, bg_image_b.tilemap, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, indexB), 121 | 0, 122 | 0, 123 | 0, 124 | 0, 125 | PLANE_MAX_TILE, 126 | 28, 127 | CPU); 128 | lastDstColB = PLANE_MAX_TILE - 1; 129 | lastSrcColB = PLANE_MAX_TILE - 1; 130 | int count = 0; 131 | while (TRUE) 132 | { 133 | s16 sPlaneOffsetA = scrollPlane(BG_A, bg_image_a.tilemap, indexA, FIX32(4.5), &offsetA, &imageOffsetA, &lastSrcColA, &lastDstColA); 134 | KLog_S2("count: ", count, " offset: ", sPlaneOffsetA); 135 | KLog_F1(" offsetA: ", offsetA); 136 | s16 sPlaneOffsetB = scrollPlane(BG_B, bg_image_b.tilemap, indexB, FIX32(0.05), &offsetB, &imageOffsetB, &lastSrcColB, &lastDstColB); 137 | 138 | VDP_setHorizontalScroll(BG_A, -sPlaneOffsetA); // negative moves plane to left, positive to right 139 | VDP_setHorizontalScroll(BG_B, -sPlaneOffsetB); 140 | 141 | // let SGDK do its thing 142 | SYS_doVBlankProcess(); 143 | ++count; 144 | } 145 | return 0; 146 | } 147 | -------------------------------------------------------------------------------- /HScrollLine/res/bg/desert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/HScrollLine/res/bg/desert.png -------------------------------------------------------------------------------- /HScrollLine/res/resources.res: -------------------------------------------------------------------------------- 1 | 2 | IMAGE desert "bg/desert.png" BEST 3 | PALETTE desert_pal "bg/desert.png" 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /HScrollLine/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | 4 | #define TOTAL_LINES 224 5 | #define FAR 89 6 | #define LAND_LINES TOTAL_LINES - FAR 7 | 8 | int main(bool hard) { 9 | 10 | ////////////////////////////////////////////////////////////// 11 | // setup screen and palettes 12 | VDP_setBackgroundColor(16); 13 | VDP_setScreenWidth320(); 14 | 15 | PAL_setPalette( PAL0, desert_pal.data, CPU ); 16 | 17 | ////////////////////////////////////////////////////////////// 18 | // setup scrolling 19 | int ind = TILE_USER_INDEX; 20 | VDP_drawImageEx(BG_B, &desert, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 21 | ind += desert.tileset->numTile; 22 | 23 | // use LINE scroll for horizontal 24 | VDP_setScrollingMode( HSCROLL_LINE, VSCROLL_PLANE); 25 | 26 | s16 hScroll[TOTAL_LINES]; 27 | memset( hScroll, 0, sizeof(hScroll)); 28 | fix16 offsetPlaneSky = FIX16(0); 29 | fix16 landOffsets[LAND_LINES]; 30 | memset( landOffsets, 0, sizeof(landOffsets)); 31 | 32 | while(TRUE) 33 | { 34 | // scroll the sky 35 | offsetPlaneSky = offsetPlaneSky - FIX16(0.05); 36 | for (u16 i = 0; i 2 | #include "resources.h" 3 | 4 | // default SGDK width is 512 pixels (64 tiles) 5 | #define PLANE_MAX_PIXEL 512 6 | #define PLANE_MAX_TILE 64 7 | 8 | // Images in this project are 1280 pixels wide ( tiles) 9 | #define IMAGE_MAX_PIXEL 1280 10 | #define IMAGE_MAX_TILE 160 11 | 12 | // 13 | // 0-2 don't scroll. no need 14 | // 3-4 fastest cloud <<<<<< START BG_B 15 | // 5 16 | // 6 17 | // 7-8 slowest cloud <<<<<< START BG_A (21 rows for A) 18 | // 9-11 the distance. slowest of all 19 | // 12-13 slowest land 20 | // 14 <<<<<< END BG_B (15 rows vor B) 21 | // 15 22 | // 16 23 | // 17-19 24 | // 20-27 fastest gorund <<<<<<< END BG_A 25 | #define ROWS_A 21 26 | #define START_ROW_A 7 27 | fix32 speedA[ROWS_A]; 28 | fix32 planeOffsetA[ROWS_A]; 29 | fix32 imageOffsetA[ROWS_A]; 30 | u16 lastSrcColA[ROWS_A]; 31 | u16 lastDstColA[ROWS_A]; 32 | s16 scrollA[ROWS_A]; 33 | 34 | #define ROWS_B 12 35 | #define START_ROW_B 3 36 | fix32 speedB[ROWS_B]; 37 | fix32 planeOffsetB[ROWS_B]; 38 | fix32 imageOffsetB[ROWS_B]; 39 | u16 lastSrcColB[ROWS_B]; 40 | u16 lastDstColB[ROWS_B]; 41 | s16 scrollB[ROWS_B]; 42 | 43 | void updateTiles(VDPPlane plane, const TileMap *tilemap, int index, 44 | fix32 *speed, 45 | fix32 *planeOffset, 46 | fix32 *imageOffset, 47 | u16 *lastSrcCol, 48 | u16 *lastDstCol, 49 | s16 *scroll, 50 | u16 startRow, 51 | u16 rows) 52 | { 53 | 54 | for (s16 row = 0; row < rows; ++row) 55 | { 56 | // Set the scrolling position of plane per row 57 | planeOffset[row] = planeOffset[row] + speed[row]; 58 | if (planeOffset[row] >= FIX32(PLANE_MAX_PIXEL)) // plane in memory is 512 pixels wide 59 | { 60 | planeOffset[row] = FIX32(0); 61 | } 62 | 63 | // keep track of where we are in the image per row 64 | imageOffset[row] = imageOffset[row] + speed[row]; 65 | if (imageOffset[row] >= FIX32(IMAGE_MAX_PIXEL)) // bg image is 1280 pixels wide 66 | { 67 | imageOffset[row] = FIX32(0); 68 | } 69 | s16 sPlaneOffset = fix32ToInt(planeOffset[row]); 70 | // check if we need a new tile 71 | if (sPlaneOffset % 8 == 0) 72 | { 73 | // get destination column (in tiles) 74 | s16 dstCol = (sPlaneOffset + PLANE_MAX_PIXEL - 8) / 8; 75 | if (dstCol >= PLANE_MAX_TILE) 76 | { 77 | dstCol -= PLANE_MAX_TILE; // wrap around to the start of the plane 78 | } 79 | 80 | // get source column (in tiles) 81 | s16 sImageOffset = fix32ToInt(imageOffset[row]); 82 | s16 srcCol = (sImageOffset + PLANE_MAX_PIXEL - 8) / 8; 83 | if (srcCol >= IMAGE_MAX_TILE) 84 | { 85 | srcCol -= IMAGE_MAX_TILE; // wrap around to the start of the image 86 | } 87 | // if the current destination column is smaller n 88 | // the last one, the region is *notnuous 89 | // Two or more tile moves may be required. 90 | if (dstCol != lastDstCol[row]) 91 | { 92 | s16 width = dstCol - lastDstCol[row]; 93 | if (width < 0) 94 | { 95 | width += PLANE_MAX_TILE; 96 | } 97 | // just loop for now ( probably inefficient if there are a lot of columns to copy) 98 | s16 tmpDst = lastDstCol[row] + 1; 99 | s16 tmpSrc = lastSrcCol[row] + 1; 100 | for (s16 i = 0; i < width; ++i) 101 | { 102 | if (tmpDst >= PLANE_MAX_TILE) 103 | { 104 | tmpDst = 0; 105 | } 106 | if (tmpSrc >= IMAGE_MAX_TILE) 107 | { 108 | tmpSrc = 0; 109 | } 110 | VDP_setTileMapEx(plane, tilemap, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, index), 111 | tmpDst, // Plane X destination 112 | startRow + row, // plane Y destination 113 | tmpSrc, // Region X start position 114 | startRow + row, // Region Y start position 115 | 1, // width 116 | 1, // height 117 | CPU); 118 | tmpDst++; 119 | tmpSrc++; 120 | } 121 | lastDstCol[row] = tmpDst - 1; 122 | lastSrcCol[row] = tmpSrc - 1; 123 | } 124 | } 125 | scroll[row] = -sPlaneOffset; 126 | } 127 | } 128 | 129 | void setupA() 130 | { 131 | // setup scrolling vals 132 | for (int row = 0; row < ROWS_A; ++row) 133 | { 134 | planeOffsetA[row] = FIX32(0); 135 | imageOffsetA[row] = FIX32(0); 136 | lastSrcColA[row] = PLANE_MAX_TILE - 1; 137 | lastDstColA[row] = PLANE_MAX_TILE - 1; 138 | scrollA[row] = 0; 139 | } 140 | // 7-15 are 2nd slowest land value 141 | for (int row = 7; row <= 16; ++row) 142 | { 143 | speedA[row - START_ROW_A] = FIX32(2.2); 144 | } 145 | // 17-19 146 | for (int row = 17; row <= 19; ++row) 147 | { 148 | speedA[row - START_ROW_A] = FIX32(3.5); 149 | } 150 | // 20-27 fastest ground 151 | for (int row = 20; row < 23; ++row) 152 | { 153 | speedA[row - START_ROW_A] = FIX32(4.7); 154 | } 155 | for (int row = 23; row <= 27; ++row) 156 | { 157 | speedA[row - START_ROW_A] = FIX32(7.6); 158 | } 159 | } 160 | 161 | void setupB() 162 | { 163 | // setup scrolling vals 164 | for (int row = 0; row < ROWS_B; ++row) 165 | { 166 | planeOffsetB[row] = FIX32(0); 167 | imageOffsetB[row] = FIX32(0); 168 | lastSrcColB[row] = PLANE_MAX_TILE - 1; 169 | lastDstColB[row] = PLANE_MAX_TILE - 1; 170 | scrollB[row] = 0; 171 | } 172 | // 3-4 fastest cloud 173 | speedB[0] = FIX32(6); 174 | speedB[1] = FIX32(6); 175 | // 5 176 | speedB[2] = FIX32(3); 177 | // 6 178 | speedB[3] = FIX32(1.5); 179 | // 7-8 slowest cloud 180 | speedB[4] = FIX32(0.75); 181 | speedB[5] = FIX32(0.75); 182 | // 9-11 the distances 183 | speedB[6] = FIX32(0.075); 184 | speedB[7] = FIX32(0.075); 185 | speedB[8] = FIX32(0.075); 186 | // 12-13 slow land 187 | speedB[9] = FIX32(0.5); 188 | speedB[10] = FIX32(1.0); 189 | // 14 190 | speedB[11] = FIX32(1.5); 191 | } 192 | 193 | int main(bool hard) 194 | { 195 | //setup values 196 | setupA(); 197 | setupB(); 198 | 199 | // set color palette 0 200 | PAL_setPalette(PAL0, bg_palette_a.data, CPU); 201 | 202 | // set scrolling mode to TILE for horizontal. 203 | VDP_setScrollingMode(HSCROLL_TILE, VSCROLL_PLANE); 204 | 205 | // get tile positions in VRAM. 206 | int ind = TILE_USER_INDEX; 207 | int indexA = ind; 208 | // Load the plane tiles into VRAM 209 | VDP_loadTileSet(bg_image_a.tileset, ind, DMA); 210 | int indexB = ind + bg_image_a.tileset->numTile; // new 211 | VDP_loadTileSet(bg_image_b.tileset, indexB, DMA); 212 | 213 | // put out the image 214 | VDP_setTileMapEx(BG_A, bg_image_a.tilemap, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, indexA), 215 | 0, // Plane X destination 216 | 0, // plane Y destination 217 | 0, // Region X start position 218 | 0, // Region Y start position 219 | PLANE_MAX_TILE, // width (went with 64 becasue default width is 64. Viewable screen is 40) 220 | 28, // height 221 | CPU); 222 | VDP_setTileMapEx(BG_B, bg_image_b.tilemap, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, indexB), 223 | 0, 224 | 0, 225 | 0, 226 | 0, 227 | PLANE_MAX_TILE, 228 | 28, 229 | CPU); 230 | 231 | while (TRUE) 232 | { 233 | updateTiles(BG_A, bg_image_a.tilemap, indexA, 234 | speedA, 235 | planeOffsetA, 236 | imageOffsetA, 237 | lastSrcColA, 238 | lastDstColA, 239 | scrollA, 240 | START_ROW_A, 241 | ROWS_A); 242 | VDP_setHorizontalScrollTile(BG_A, START_ROW_A, scrollA, ROWS_A, CPU); 243 | 244 | updateTiles(BG_B, bg_image_b.tilemap, indexB, 245 | speedB, 246 | planeOffsetB, 247 | imageOffsetB, 248 | lastSrcColB, 249 | lastDstColB, 250 | scrollB, 251 | START_ROW_B, 252 | ROWS_B); 253 | VDP_setHorizontalScrollTile(BG_B, START_ROW_B, scrollB, ROWS_B, CPU); 254 | // let SGDK do its thing 255 | SYS_doVBlankProcess(); 256 | } 257 | return 0; 258 | } 259 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Greg Gallardo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SGDK_Scrolling 2 | This repo contains code I've written while trying out different ways of scrolling with [SGDK](https://github.com/Stephane-D/SGDK). 3 | 4 | THis code has been tested with [SGDK 1.80](https://github.com/Stephane-D/SGDK/releases/tag/v1.80). 5 | 6 | ## MAP for Large Images 7 | The folders *ScrollingWithCamera* and *SimpleScrolling* are scrolling examples using the MAP type introduced in version [1.60](https://github.com/Stephane-D/SGDK/releases/tag/v1.60). The code has been updated to compile with SGDK 1.80. So you must have version 1.80 on your system to use this code. 8 | 9 | ## Image Scrolling with `VDP_setTileMap()` 10 | * *HScroll* uses `VDP_setTileMapEx()` to scroll a relatively large image over the default SGDK tilespace. 11 | * *HScrollDec* uses `VDP_setTileMapEx()` and `HSCROLL_PLANE` to scroll two planes. 12 | * *HScrollTiled* uses `VDP_setTileMapEx()` and `HSCROLL_TILE` to demonstrate parallax scrolling with two planes. 13 | 14 | -------------------------------------------------------------------------------- /RotatePy/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Scroll Rotation Values Script 3 | `sgdk_scroll_rotate.py` is a script that pre-calculates horizontal and vertical scrolling 4 | values to simulate full screen rotation with SGDK's scrolling functions. It will calculate 5 | scrolling offsets for a range of rotation values specified by the user. The values 6 | are stored in s16 arrays for use with `VDP_setHorizontalScrollLine()` and 7 | `VDP_setVerticalScrollTile()`. 8 | 9 | 10 | # TODO 11 | * Check input values for invalid combinations. 12 | * Give the option to get scrolling functions instead of pre-calculated arrays. 13 | * Move `rotation.h` generation code to a template file. 14 | 15 | # Dependencies 16 | The script was written for [Python3](https://www.python.org/downloads/) and requires 17 | [NumPy](https://numpy.org/) and [Jinja](https://palletsprojects.com/p/jinja/). 18 | 19 | 20 | I'm using WSL and Ubuntu 22.02 on my development machine, but I imagine 21 | most people will be using Windows. I'm going to keep on using WSL, but 22 | I have installed the windows versions to test the script. 23 | 1. Get Python3. I used the Microsoft Store version. 24 | 25 | 2. Install the dependencies. I opened a command line window and typed: 26 | ```cmd 27 | pip3 install numpy 28 | pip3 install jinja2 29 | ``` 30 | 3. Run the script with: 31 | ```cmd 32 | python3 sgdk_scroll_rotate.py 33 | ``` 34 | 35 | 36 | # Basic Usage 37 | ```txt 38 | usage: sgdk_scroll_rotate.py [-h] [-v] [-s ARG] [-e ARG] [-i ARG] [-c ARG] [-C ARG] [-x ARG] [-w ARG] [-r ARG] [-R ARG] [-y ARG] [-o ARG] [-P ARG] [-p ARG] [-b ARG] [-t ARG] [-S ARG] 39 | 40 | Generates rotation arrays for SGDK. 41 | 42 | options: 43 | -h, --help show this help message and exit 44 | -v, --verbose Print debug messages 45 | -s ARG, --start_angle ARG 46 | Starting rotation in degrees 47 | -e ARG, --end_angle ARG 48 | End rotation angle in degrees 49 | -i ARG, --angle_increment ARG 50 | Rotation step size 51 | -c ARG, --column_start ARG 52 | First column to rotate (default 0) 53 | -C ARG, --column_end ARG 54 | Last column to rotate (default 19) 55 | -x ARG, --center_x ARG 56 | Which column is the center of rotation 57 | -w ARG, --image_width ARG 58 | Width of image to rotate 59 | -r ARG, --row_start ARG 60 | First row to rotate 61 | -R ARG, --row_end ARG 62 | Last row to rotate 63 | -y ARG, --center_y ARG 64 | Which row is the center of rotation 65 | -o ARG, --output_filename ARG 66 | Output filename 67 | -P ARG, --prefix ARG Add a prefix to array names 68 | -p ARG, --project_directory ARG 69 | Create project directory with resource files and simple SGDK code. 70 | -b ARG, --background_filename ARG 71 | Specify background image 72 | -t ARG, --points_filename ARG 73 | Specify CSV file with points to rotate along with bg 74 | -S ARG, --sprite_filename ARG 75 | Specify target sprite image 76 | ``` 77 | 78 | 79 | 80 | 81 | # Examples 82 | 83 | ## Rotate 2 Degrees 84 | The simplest use case is to generate a single set of rotation offsets. This is not really useful. 85 | If just want your background image rotated by a fixed angle, you're better off drawing it that way. 86 | I'm using it here for simplicity 87 | 88 | 89 | Suppose you want to have a simple image of a green bar. 90 | ![green bar](https://raw.githubusercontent.com/radioation/SGDK_Scrolling/main/RotatePy/green_bar.png) 91 | 92 | This image is 320 x 224 pixels. The green bar's upper left pixel is at `x = 48` and `y = 96`. The 93 | lower right corner of the green bar is at `x = 271` and `y = 111`. 94 | 95 | 96 | To make things easier to read I'll just do rotate the image by 2 degrees. We only have to horizontally scroll the lines with pixels in them. Using a small rotation in this example limits the size of the output files. To limit the computed range to 2 degrees use: 97 | ```c 98 | --start_angle 2 99 | --end_angle 2 100 | ``` 101 | 102 | 103 | For this example, I want the center of rotation to be near the center of the bar. This is approximately 104 | at (x = 159, y = 103). Recall the Genesis scrolling columns are 16 pixels wide and 159/16 is ~9.9375. 105 | I'm going to set the center X value to 9 ( arrays indices start with 0 in C) 106 | ```c 107 | --center_x 9 108 | --center_y 103 109 | ``` 110 | 111 | 112 | By default, my script will generate offsets for all 223 rows of the genesis screen. Calculating offsets 113 | for every row is be pretty wasteful for this image. We can limit the number of rows with 114 | ```c 115 | --row_start 93 116 | --row_end 113 117 | ``` 118 | 119 | ```bash 120 | python3 sgdk_scroll_rotate.py --start_angle 2 --end_angle 2 --center_x 9 --center_y 103 --row_start 93 --row_end 113 121 | ``` 122 | The output will look something like: 123 | 124 | ```cmd 125 | C:\Dev\Rotate> python3 sgdk_scroll_rotate.py --start_angle 2 --end_angle 2 --center_x 9 --center_y 103 --row_start 93 --row_end 113 126 | INFO: Parameters 127 | INFO: Start angle: 2.000000 Stop angle: 2.000000 Step size: 1.000000 128 | INFO: Columns to rotate: 20 Center column: 9 129 | INFO: Rows to rotate: 21 Center row: 103 130 | #define ROWS_A 21 131 | #define START_ROW_A 93 132 | #define END_ROW_A 113 133 | #define COLS_A 20 134 | #define START_COL_A 0 135 | #define END_COL_A 19 136 | ``` 137 | The output tells you what parameters were used. 138 | 139 | 140 | The script also generates a file called `rotation.h`. It should look something like this: 141 | ```c 142 | #ifndef _ROTATION_H_ 143 | #define _ROTATION_H_ 144 | 145 | 146 | #define _SCROLL_COUNT 1 147 | #define ROWS_A 21 148 | #define START_ROW_A 93 149 | #define END_ROW_A 113 150 | #define COLS_A 20 151 | #define START_COL_A 0 152 | #define END_COL_A 19 153 | 154 | 155 | s16 _hScroll[] = { 156 | // rotation values for angle 2.000000 starts at 0 157 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 158 | 159 | 160 | s16 _vScroll[] = { 161 | // rotation values for angle 2.000000 starts at 0 162 | -5, -4, -4, -3, -3, -2, -2, -1, -1, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6 163 | }; 164 | 165 | 166 | #endif // _ROTATION_H_ 167 | ``` 168 | *`_SCROLL_COUNT` lets you know how many sets of offsets are in your file. In this case, it's 1 as we only generated values for one angle (2 degrees). 169 | * The `#define` rows can be used by your code when applying the scrolling offset arrays. 170 | * `_hScroll[]` is the set of horizontal scrolling offsets. 171 | *`_vScroll[]` is the set of vertical scrolling offsets. 172 | 173 | 174 | "rotation.h" can be included directly in your source code. Depending on your project, you may want to define the scrolling arrays in .c file instead of a header. YMMV. 175 | 176 | ```C 177 | #include "rotation.h" 178 | ``` 179 | 180 | You'll want to make sure the scrolling mode is set to `HSCROLL_LINE` and `VSCROLL_2TILE`. 181 | ```c 182 | // set scrolling mode to LINE for horizontal and TILE for vertical 183 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_2TILE); 184 | ``` 185 | 186 | You tell SGDK to apply the scrolling values with `VDP_setHorizontalScrollLine()` and 187 | `VDP_setVerticalScrollTile()` 188 | ```c 189 | VDP_setHorizontalScrollLine(BG_A, START_ROW_A, _hScroll, ROWS_A, CPU); 190 | VDP_setVerticalScrollTile(BG_A, START_COL_A, _vScroll, COLS_A, CPU); 191 | ``` 192 | 193 | Project files can be found [here](https://github.com/radioation/SGDK_Scrolling/tree/main/RotatePy/rotate_2_degrees) 194 | 195 | 196 | The rotated image should look like: 197 | ![green bar rotated 2 degrees](https://raw.githubusercontent.com/radioation/SGDK_Scrolling/main/RotatePy/green_bar_rotated_2.png) 198 | 199 | 200 | 201 | The start/stop rows and columns will vary with your application. You'll need to adjust 202 | rows and columns to fit your image and the amount you want to rotate it. 203 | If you have too few rows, the rotation effect will stop too soon. 5 degrees with 204 | `--row_start 93 --row_end 113` looks like this: 205 | ![green bar rotated 5 degrees with too few start and end rows](https://raw.githubusercontent.com/radioation/SGDK_Scrolling/main/RotatePy/green_bar_rotated_5_too_few_rows.png) 206 | 207 | 208 | 209 | Increasing the number of computed rows lets you use larger rotations. 5 degrees using 210 | `--row_start 63 --row_end 143` looks like this: 211 | ![green bar rotated 5 degrees with more start and end rows](https://raw.githubusercontent.com/radioation/SGDK_Scrolling/main/RotatePy/green_bar_rotated_5_MOAR_ROWS.png) 212 | -15 degrees 213 | 214 | And 15 degrees looks like this. 215 | ![green bar rotated 15 degrees with more start and end rows](https://raw.githubusercontent.com/radioation/SGDK_Scrolling/main/RotatePy/green_bar_rotated_15.png) 216 | 217 | Note the green pixels on the left portion of the display. You can around this by using a wider image for the rotating background. The actual width of your background image depends on what your doing. 218 | 219 | 220 | 221 | ## Generate a Range of Rotations. 222 | 223 | It's a waste of memory and CPU to do a single rotation on a image. You'd be better off creating the 224 | background image pre-rotated. In this example, I want to rotate the image 225 | from -5 degrees to 5 degrees in increments of 1 degree. To do this, I used the following values for the angles: 226 | ```c 227 | --start_angle -5 228 | --end_angle 5 229 | --angle_increment 1 230 | ``` 231 | The command and output looks something like this: 232 | ```cmd 233 | python3 sgdk_scroll_rotate.py --start_angle -5 --end_angle 5 --angle_increment 1 --center_x 9 --center_y 103 --row_start 73 --row_end 133 234 | INFO: Parameters 235 | INFO: Start angle: -5.000000 Stop angle: 5.000000 Step size: 1.000000 236 | INFO: Columns to rotate: 20 Center column: 9 237 | INFO: Rows to rotate: 61 Center row: 103 238 | #define ROWS_A 61 239 | #define START_ROW_A 73 240 | #define END_ROW_A 133 241 | #define COLS_A 20 242 | #define START_COL_A 0 243 | #define END_COL_A 19 244 | ``` 245 | 246 | and the `rotation.h` offset file now look like: 247 | ```c 248 | #ifndef _ROTATION_H_ 249 | #define _ROTATION_H_ 250 | 251 | 252 | #define _SCROLL_COUNT 11 253 | #define ROWS_A 61 254 | #define START_ROW_A 73 255 | #define END_ROW_A 133 256 | #define COLS_A 20 257 | #define START_COL_A 0 258 | #define END_COL_A 19 259 | 260 | 261 | s16 _hScroll[] = { 262 | // rotation values for angle -5.000000 starts at 0 263 | 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -3, -3 264 | // rotation values for angle -4.000000 starts at 61 265 | , 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2 266 | // rotation values for angle -3.000000 starts at 122 267 | , 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2 268 | // rotation values for angle -2.000000 starts at 183 269 | , 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 270 | // rotation values for angle -1.000000 starts at 244 271 | , 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1 272 | // rotation values for angle 0.000000 starts at 305 273 | , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 274 | // rotation values for angle 1.000000 starts at 366 275 | , -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 276 | // rotation values for angle 2.000000 starts at 427 277 | , -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 278 | // rotation values for angle 3.000000 starts at 488 279 | , -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2 280 | // rotation values for angle 4.000000 starts at 549 281 | , -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2 282 | // rotation values for angle 5.000000 starts at 610 283 | , -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3}; 284 | 285 | 286 | s16 _vScroll[] = { 287 | // rotation values for angle -5.000000 starts at 0 288 | 13, 11, 10, 8, 7, 6, 4, 3, 1, 0, -1, -3, -4, -6, -7, -8, -10, -11, -13, -14 289 | // rotation values for angle -4.000000 starts at 20 290 | , 10, 9, 8, 7, 6, 4, 3, 2, 1, 0, -1, -2, -3, -4, -6, -7, -8, -9, -10, -11 291 | // rotation values for angle -3.000000 starts at 40 292 | , 8, 7, 6, 5, 4, 3, 3, 2, 1, 0, -1, -2, -3, -3, -4, -5, -6, -7, -8, -8 293 | // rotation values for angle -2.000000 starts at 60 294 | , 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -6 295 | // rotation values for angle -1.000000 starts at 80 296 | , 3, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, -1, -1, -1, -1, -2, -2, -2, -3, -3 297 | // rotation values for angle 0.000000 starts at 100 298 | , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 299 | // rotation values for angle 1.000000 starts at 120 300 | , -3, -2, -2, -2, -1, -1, -1, -1, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3 301 | // rotation values for angle 2.000000 starts at 140 302 | , -5, -4, -4, -3, -3, -2, -2, -1, -1, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6 303 | // rotation values for angle 3.000000 starts at 160 304 | , -8, -7, -6, -5, -4, -3, -3, -2, -1, 0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 8 305 | // rotation values for angle 4.000000 starts at 180 306 | , -10, -9, -8, -7, -6, -4, -3, -2, -1, 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11 307 | // rotation values for angle 5.000000 starts at 200 308 | , -13, -11, -10, -8, -7, -6, -4, -3, -1, 0, 1, 3, 4, 6, 7, 8, 10, 11, 13, 14 309 | }; 310 | 311 | 312 | #endif // _ROTATION_H_ 313 | ``` 314 | 315 | It's a much larger file now because it's calculating 11 different sets of scrolling 316 | values. To help keep track of the data in the arrays 317 | `_hScroll` and `_vScroll` have comments that indicate where each set of angles 318 | begins. 319 | 320 | 321 | 322 | 323 | 324 | 325 | Each set of offsets for `_hScroll` uses an array element for reach row to be scrolled. In this example 326 | `ROWS_A` is defined as 61. 327 | ```c 328 | `#define ROWS_A 61` 329 | ``` 330 | The comments for `_hScroll` show the starting position for each angle: -5.0 degrees at 0, -4.0 degrees at 61, -3.0 degrees at 122, etc. 331 | ```c 332 | s16 _hScroll[] = { 333 | // rotation values for angle -5.000000 starts at 0 334 | ... 335 | // rotation values for angle -4.000000 starts at 61 336 | ... 337 | // rotation values for angle -3.000000 starts at 122 338 | 339 | ... 340 | 341 | ``` 342 | 343 | `_vScroll` uses an array element for for each column in the set. `COLS_A` for this 344 | example is defined as 20 345 | ```c 346 | #define COLS_A 20 347 | ``` 348 | The comments for `_vScroll` show the starting position for each angle: -5.0 degrees at 0, -4.0 degrees at 20, -3.0 degrees at 40, etc. 349 | ```c 350 | s16 _vScroll[] = { 351 | // rotation values for angle -5.000000 starts at 0 352 | 13, 11, 10, 8, 7, 6, 4, 3, 1, 0, -1, -3, -4, -6, -7, -8, -10, -11, -13, -14 353 | // rotation values for angle -4.000000 starts at 20 354 | , 10, 9, 8, 7, 6, 4, 3, 2, 1, 0, -1, -2, -3, -4, -6, -7, -8, -9, -10, -11 355 | // rotation values for angle -3.000000 starts at 40 356 | , 8, 7, 6, 5, 4, 3, 3, 2, 1, 0, -1, -2, -3, -3, -4, -5, -6, -7, -8, -8 357 | 358 | ... 359 | 360 | ``` 361 | 362 | You can quickly rotate a background by copying the 363 | right position of the array to SGDK's scrolling functions. 364 | If we want to rotate by -5 degreees, it's the set of the array, So we just need to copy 365 | scroll values from the start of the arrays. It could be done with code like this: 366 | ```c 367 | // Copy first set of angle offsets to local arrays 368 | memcpy(hScrollA, _hScroll, ROWS_A*sizeof(s16)); 369 | memcpy(vScrollA, _vScroll, COLS_A*sizeof(s16)); 370 | 371 | // set SGDK scrolling functions with local arrays to fake the rotaiton. 372 | VDP_setHorizontalScrollLine(BG_A, START_ROW_A, hScrollA, ROWS_A, CPU); 373 | VDP_setVerticalScrollTile(BG_A, START_COL_A, vScrollA, COLS_A, CPU); 374 | ``` 375 | If you want the rotation to be 0 degrees, use position 305 of `_hScroll`. 376 | ```c 377 | // rotation values for angle 0.000000 starts at 305 378 | ``` 379 | and position 100 of `_vScroll`. 380 | ```c 381 | // rotation values for angle 0.000000 starts at 100 382 | ``` 383 | So you could set the scrolling lines and tiles with code like this: 384 | ```c 385 | // Copy sixth set of angle offsets to local arrays 386 | memcpy(hScrollA, _hScroll + 305, ROWS_A*sizeof(s16)); 387 | memcpy(vScrollA, _vScroll + 100, COLS_A*sizeof(s16)); 388 | 389 | // set SGDK scrolling functions with local arrays to fake the rotaiton. 390 | VDP_setHorizontalScrollLine(BG_A, START_ROW_A, hScrollA, ROWS_A, CPU); 391 | VDP_setVerticalScrollTile(BG_A, START_COL_A, vScrollA, COLS_A, CPU); 392 | ``` 393 | 394 | Of course, using hard-coded offsets is not very flexible. A better way to use the scrolling 395 | arrays is to keep track of the current angle index in a variable and 396 | add it to `_hScroll` and `_vScroll` whenever you want to change the rotation. 397 | ```c 398 | // Copy current angle offsets into local arrays 399 | memcpy(hScrollA, _hScroll + currAngle * ROWS_A, ROWS_A*sizeof(s16)); 400 | memcpy(vScrollA, _vScroll + currAngle * COLS_A, COLS_A*sizeof(s16)); 401 | ``` 402 | 403 | To rock the image back and forth though the entire range of scroll values you just need to 404 | move currAngle from 0 to 10 and from 10 to 0. 405 | ```c 406 | s16 currAngle = 5; // start at angle 0 407 | s16 stepDir = 1; // increase the angle 408 | u8 angleDelay = 0; 409 | while (TRUE) 410 | { 411 | // handle rotation 412 | ++angleDelay; 413 | if (angleDelay % 6 == 0) 414 | { 415 | currAngle += stepDir; 416 | if (currAngle >= _SCROLL_COUNT) 417 | { 418 | stepDir = -1; 419 | currAngle = 10; 420 | } 421 | else if (currAngle < 0) 422 | { 423 | stepDir = 1; 424 | currAngle = 0; 425 | } 426 | 427 | // Copy current angle offsets into local arrays 428 | memcpy(hScrollA, _hScroll + currAngle * ROWS_A, ROWS_A * sizeof(s16)); 429 | memcpy(vScrollA, _vScroll + currAngle * COLS_A, COLS_A * sizeof(s16)); 430 | } 431 | 432 | // set SGDK scrolling functions with local arrays to fake the rotation. 433 | VDP_setHorizontalScrollLine(BG_A, START_ROW_A, hScrollA, ROWS_A, CPU); 434 | VDP_setVerticalScrollTile(BG_A, START_COL_A, vScrollA, COLS_A, CPU); 435 | 436 | // let SGDK do its thing 437 | SYS_doVBlankProcess(); 438 | } 439 | ``` 440 | 441 | Project files can be found [here](https://github.com/radioation/SGDK_Scrolling/tree/main/RotatePy/rotate_-5_to_5) 442 | 443 | 444 | 445 | * the (angleDelay %6 == 0) just slows down the rate of changin `currAngle` 446 | * still getting scrolling artifacts on the left. A simple way around this is to use a wider image (nextexample) 447 | * Rotating around a fixed point may work in some cases, but you might want a more dynamic background 448 | 449 | 450 | 451 | 452 | ## Rotation and Translation 453 | The above example rotates a background image, but what if you want to move the background while 454 | its rotating? If we pass the array values directly to SGDK's scrolling functions, the rotation will always be 455 | around the origin we specified to the script. We *could* have the script calculate rotations at different positions 456 | on the screen, but I think this would get very large, very quickly. 457 | 458 | 459 | 460 | Instead of precalculating every possibility, I'll handle scrolling translation at runtime. We can add or 461 | subtract from the `_hScroll` values to move the background left and right. We can also add or subtract 462 | from the `_vScroll` values to move up and down. The code could use something like this: 463 | 464 | ```c 465 | for(int i=0; i < ROWS_A; ++i ) { 466 | hScrollA[ i ] = ship_hScroll[ currAngle * ROWS_A + i] + xOffset; 467 | } 468 | for (int i = 0; i < COLS_A; ++i) 469 | { 470 | vScrollA[i] = ship_vScroll[currAngle * COLS_A + i] + yOffset; 471 | } 472 | ``` 473 | This will be slower than a straight `memcpy()` call, but faster than calculating everything at runtime. 474 | 475 | 476 | The code is not very different from the previous example. The main difference are 477 | 1. I've added some variables to keep track of the current translation offsets. 478 | ```c 479 | s16 xOffset = 0; 480 | s16 yOffset = 0; 481 | s16 yOffsetDir = 1; 482 | u8 offsetDelay = 0; 483 | ``` 484 | 2. The main loop has some code to move the image up and down by setting `yOffset` to values from 0 to 40. It also moves the image left and right by incrementing or decrementing `xOffset` by 1 pixel based on the value of `currAngle` 485 | ```c 486 | ++offsetDelay; 487 | if( offsetDelay % 3 == 0 ) { 488 | yOffset += yOffsetDir; 489 | if( yOffset > 40) { 490 | yOffsetDir = -1; 491 | }else if( yOffset < 0 ) { 492 | yOffsetDir = 1; 493 | } 494 | if( currAngle < 4) { 495 | xOffset+=1; 496 | } else if ( currAngle > 6) { 497 | xOffset-=1; 498 | } 499 | } 500 | ``` 501 | 3. Instead of using `memcpy()` to set the angle offsets, it loops through the relevant `_hScroll` and `_vScroll` 502 | values and adds `xOffset` or `yOffset`. 503 | ```c 504 | for(int i=0; i < ROWS_A; ++i ) { 505 | hScrollA[ i ] = _hScroll[ currAngle * ROWS_A + i] + xOffset; 506 | } 507 | for (int i = 0; i < COLS_A; ++i) 508 | { 509 | vScrollA[i] = _vScroll[currAngle * COLS_A + i] + yOffset; 510 | } 511 | ``` 512 | 4. It does a sanity check on the current value of `yOffset` before setting the current horizontal scroll values. 513 | ```c 514 | s16 startHorizontalScroll = START_ROW_A - yOffset; 515 | s16 totalRows = ROWS_A ; 516 | if( startHorizontalScroll < 0 ) { 517 | totalRows = ROWS_A + startHorizontalScroll; 518 | startHorizontalScroll = 0; 519 | } 520 | VDP_setHorizontalScrollLine(BG_A, startHorizontalScroll, hScrollA, totalRows, DMA); 521 | ``` 522 | This is done to make sure we don't start the horizontal scroll at a negative row. 523 | 524 | Project files can be found [here](https://github.com/radioation/SGDK_Scrolling/tree/main/RotatePy/rotation_and_translation) 525 | 526 | 527 | 528 | 529 | -------------------------------------------------------------------------------- /RotatePy/crosshairs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotatePy/crosshairs.png -------------------------------------------------------------------------------- /RotatePy/green_bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotatePy/green_bar.png -------------------------------------------------------------------------------- /RotatePy/green_bar_rotated_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotatePy/green_bar_rotated_15.png -------------------------------------------------------------------------------- /RotatePy/green_bar_rotated_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotatePy/green_bar_rotated_2.png -------------------------------------------------------------------------------- /RotatePy/green_bar_rotated_5_MOAR_ROWS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotatePy/green_bar_rotated_5_MOAR_ROWS.png -------------------------------------------------------------------------------- /RotatePy/green_bar_rotated_5_too_few_rows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotatePy/green_bar_rotated_5_too_few_rows.png -------------------------------------------------------------------------------- /RotatePy/main.c.jinja: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | #include "{{ rotation_filename }}" 4 | 5 | ///////////////////////////////////////////////////////////////////// 6 | // Scrolling Stuff 7 | #define PLANE_MAX_TILE 64 8 | 9 | 10 | s16 hScrollA[224]; 11 | s16 vScrollA[20]; 12 | 13 | int main(bool hard) 14 | { 15 | VDP_setScreenWidth320(); 16 | // set colors 17 | PAL_setPalette( PAL0, {{ bg_name}}_pal.data, CPU ); 18 | PAL_setPalette( PAL1, {{ sprite_name }}_pal.data, CPU ); 19 | 20 | // set scrolling mode to LINE for horizontal and TILE for vertical 21 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_2TILE); 22 | 23 | // get tile positions in VRAM. 24 | int ind = TILE_USER_INDEX; 25 | int indexA = ind; 26 | // Load the plane tiles into VRAM 27 | VDP_loadTileSet({{bg_name}}.tileset, ind, DMA); 28 | 29 | // setup the tiles 30 | VDP_setTileMapEx(BG_A, {{ bg_name }}.tilemap, TILE_ATTR_FULL(PAL0, TRUE, FALSE, FALSE, indexA), 31 | 0, // Plane X destination 32 | 0, // plane Y destination 33 | 0, // Region X start position 34 | 0, // Region Y start position 35 | PLANE_MAX_TILE, // width (went with 64 becasue default width is 64. Viewable screen is 40) 36 | 28, // height 37 | CPU); 38 | 39 | 40 | s16 currAngle = 0; 41 | s16 stepDir = 1; 42 | u8 angleDelay = 0; 43 | 44 | s16 xOffset = 0; 45 | s16 yOffset = 0; 46 | s16 yOffsetDir = 1; 47 | u8 offsetDelay = 0; 48 | 49 | {% if target_list|length > 0 %} 50 | SPR_init(); 51 | {% endif %} 52 | 53 | 54 | {% for name in target_list %} 55 | Sprite * {{name}}_sprite = NULL; 56 | int {{name}}_pos_x = {{name}}[currAngle*2]-8; 57 | int {{name}}_pos_y = {{name}}[currAngle*2+1]-8; 58 | {{name}}_sprite = SPR_addSprite( &crosshairs, {{name}}_pos_x, {{name}}_pos_y, TILE_ATTR( PAL1, 1, FALSE, FALSE )); 59 | {% endfor %} 60 | 61 | while (TRUE) 62 | { 63 | 64 | // handle rotation 65 | ++angleDelay; 66 | if( angleDelay % 6 == 0 ) { 67 | currAngle += stepDir; 68 | if( currAngle >= {{ prefix }}_SCROLL_COUNT-2 ) { 69 | stepDir = -1; 70 | currAngle = 9; 71 | }else if (currAngle <0+2 ) { 72 | stepDir = 1; 73 | currAngle = 1; 74 | } 75 | } 76 | ++offsetDelay; 77 | if( offsetDelay % 3 == 0 ) { 78 | yOffset += yOffsetDir; 79 | if( yOffset > 40) { 80 | yOffsetDir = -1; 81 | }else if( yOffset < 0 ) { 82 | yOffsetDir = 1; 83 | } 84 | if( currAngle < 4) { 85 | xOffset+=2; 86 | } else if ( currAngle > 6) { 87 | xOffset-=2; 88 | } 89 | } 90 | 91 | 92 | // could unroll loops to eliminate some overhead 93 | for(int i=0; i < {{ prefix }}_ROWS_A; ++i ) { 94 | hScrollA[ i ] = {{ prefix }}_hScroll[ currAngle * {{ prefix }}_ROWS_A + i] + xOffset; 95 | } 96 | for (int i = 0; i < {{prefix}}_COLS_A; ++i) 97 | { 98 | vScrollA[i] = {{ prefix }}_vScroll[currAngle * {{prefix}}_COLS_A + i] + yOffset; 99 | } 100 | 101 | {% for name in target_list %} 102 | {{name}}_pos_x = {{name}}[currAngle * 2]-8 + xOffset; 103 | {{name}}_pos_y = {{name}}[currAngle * 2 + 1]-8 - yOffset; 104 | SPR_setPosition({{name}}_sprite, {{name}}_pos_x, {{name}}_pos_y); 105 | {% endfor %} 106 | 107 | {% if target_list|length > 0 %} 108 | SPR_update(); 109 | {% endif %} 110 | 111 | // set SGDK scrolling functions to fake the rotaiton. 112 | s16 startHorizontalScroll = {{ prefix }}_START_ROW_A - yOffset; 113 | s16 totalRows = {{ prefix }}_ROWS_A ; 114 | if( startHorizontalScroll < 0 ) { 115 | totalRows = {{ prefix }}_ROWS_A + startHorizontalScroll; 116 | startHorizontalScroll = 0; 117 | } 118 | VDP_setHorizontalScrollLine(BG_A, startHorizontalScroll, hScrollA, totalRows, DMA); 119 | VDP_setVerticalScrollTile(BG_A, {{ prefix }}_START_COL_A, vScrollA, {{prefix}}_COLS_A, DMA); 120 | 121 | // let SGDK do its thing 122 | SYS_doVBlankProcess(); 123 | } 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /RotatePy/platform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotatePy/platform.png -------------------------------------------------------------------------------- /RotatePy/points.csv: -------------------------------------------------------------------------------- 1 | name,x,y 2 | lgun,106,130 3 | rgun,277,130 4 | lvent,176,127 5 | rvent,207,127 6 | -------------------------------------------------------------------------------- /RotatePy/resources.res.jinja: -------------------------------------------------------------------------------- 1 | IMAGE {{ bg_name }} "bg/{{ bg_name }}.png" NONE 2 | SPRITE {{ sprite_name }} "sprites/{{ sprite_name }}.png" 2 2 NONE 3 | 4 | PALETTE {{ bg_name }}_pal "bg/{{ bg_name }}.png" 5 | PALETTE {{ sprite_name }}_pal "sprites/{{ sprite_name }}.png" 6 | -------------------------------------------------------------------------------- /RotatePy/rotate_-5_to_5/res/bg/platform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotatePy/rotate_-5_to_5/res/bg/platform.png -------------------------------------------------------------------------------- /RotatePy/rotate_-5_to_5/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE platform "bg/platform.png" NONE 2 | PALETTE platform_pal "bg/platform.png" -------------------------------------------------------------------------------- /RotatePy/rotate_-5_to_5/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | #include "rotation.h" 4 | 5 | ///////////////////////////////////////////////////////////////////// 6 | // Scrolling Stuff 7 | #define PLANE_MAX_TILE 64 8 | 9 | s16 hScrollA[224]; 10 | s16 vScrollA[20]; 11 | 12 | 13 | int main(bool hard) 14 | { 15 | memset(hScrollA, 0, sizeof(hScrollA)); 16 | memset(vScrollA, 0, sizeof(vScrollA)); 17 | VDP_setScreenWidth320(); 18 | // set colors 19 | PAL_setPalette( PAL0, platform_pal.data, CPU ); 20 | 21 | // set scrolling mode to LINE for horizontal and TILE for vertical 22 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_COLUMN); 23 | 24 | // get tile positions in VRAM. 25 | int ind = TILE_USER_INDEX; 26 | int indexA = ind; 27 | // Load the plane tiles into VRAM 28 | VDP_loadTileSet(platform.tileset, ind, DMA); 29 | 30 | // setup the tiles 31 | VDP_setTileMapEx(BG_A, platform.tilemap, TILE_ATTR_FULL(PAL0, TRUE, FALSE, FALSE, indexA), 32 | 0, // Plane X destination 33 | 0, // plane Y destination 34 | 0, // Region X start position 35 | 0, // Region Y start position 36 | PLANE_MAX_TILE, // width (went with 64 becasue default width is 64. Viewable screen is 40) 37 | 28, // height 38 | CPU); 39 | 40 | s16 currAngle = 5; 41 | s16 stepDir = 1; 42 | u8 angleDelay = 0; 43 | while (TRUE) 44 | { 45 | // handle rotation 46 | ++angleDelay; 47 | if (angleDelay % 6 == 0) 48 | { 49 | currAngle += stepDir; 50 | if (currAngle >= _SCROLL_COUNT) 51 | { 52 | stepDir = -1; 53 | currAngle = 10; 54 | } 55 | else if (currAngle < 0) 56 | { 57 | stepDir = 1; 58 | currAngle = 0; 59 | } 60 | 61 | // Copy current angle offsets into local arrays 62 | memcpy(hScrollA, _hScroll + currAngle * ROWS_A, ROWS_A * sizeof(s16)); 63 | memcpy(vScrollA, _vScroll + currAngle * COLS_A, COLS_A * sizeof(s16)); 64 | } 65 | 66 | // set SGDK scrolling functions with local arrays to fake the rotation. 67 | VDP_setHorizontalScrollLine(BG_A, START_ROW_A, hScrollA, ROWS_A, CPU); 68 | VDP_setVerticalScrollTile(BG_A, START_COL_A, vScrollA, COLS_A, CPU); 69 | 70 | // let SGDK do its thing 71 | SYS_doVBlankProcess(); 72 | } 73 | return 0; 74 | } -------------------------------------------------------------------------------- /RotatePy/rotate_-5_to_5/src/rotation.h: -------------------------------------------------------------------------------- 1 | #ifndef _ROTATION_H_ 2 | #define _ROTATION_H_ 3 | 4 | 5 | #define _SCROLL_COUNT 11 6 | #define ROWS_A 61 7 | #define START_ROW_A 73 8 | #define END_ROW_A 133 9 | #define COLS_A 20 10 | #define START_COL_A 0 11 | #define END_COL_A 19 12 | 13 | 14 | s16 _hScroll[] = { 15 | // rotation values for angle -5.000000 starts at 0 16 | 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -3, -3 17 | // rotation values for angle -4.000000 starts at 61 18 | , 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2 19 | // rotation values for angle -3.000000 starts at 122 20 | , 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2 21 | // rotation values for angle -2.000000 starts at 183 22 | , 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 23 | // rotation values for angle -1.000000 starts at 244 24 | , 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1 25 | // rotation values for angle 0.000000 starts at 305 26 | , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 27 | // rotation values for angle 1.000000 starts at 366 28 | , -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 29 | // rotation values for angle 2.000000 starts at 427 30 | , -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 31 | // rotation values for angle 3.000000 starts at 488 32 | , -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2 33 | // rotation values for angle 4.000000 starts at 549 34 | , -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2 35 | // rotation values for angle 5.000000 starts at 610 36 | , -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3}; 37 | 38 | 39 | s16 _vScroll[] = { 40 | // rotation values for angle -5.000000 starts at 0 41 | 13, 11, 10, 8, 7, 6, 4, 3, 1, 0, -1, -3, -4, -6, -7, -8, -10, -11, -13, -14 42 | // rotation values for angle -4.000000 starts at 20 43 | , 10, 9, 8, 7, 6, 4, 3, 2, 1, 0, -1, -2, -3, -4, -6, -7, -8, -9, -10, -11 44 | // rotation values for angle -3.000000 starts at 40 45 | , 8, 7, 6, 5, 4, 3, 3, 2, 1, 0, -1, -2, -3, -3, -4, -5, -6, -7, -8, -8 46 | // rotation values for angle -2.000000 starts at 60 47 | , 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -6 48 | // rotation values for angle -1.000000 starts at 80 49 | , 3, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, -1, -1, -1, -1, -2, -2, -2, -3, -3 50 | // rotation values for angle 0.000000 starts at 100 51 | , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 52 | // rotation values for angle 1.000000 starts at 120 53 | , -3, -2, -2, -2, -1, -1, -1, -1, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3 54 | // rotation values for angle 2.000000 starts at 140 55 | , -5, -4, -4, -3, -3, -2, -2, -1, -1, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6 56 | // rotation values for angle 3.000000 starts at 160 57 | , -8, -7, -6, -5, -4, -3, -3, -2, -1, 0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 8 58 | // rotation values for angle 4.000000 starts at 180 59 | , -10, -9, -8, -7, -6, -4, -3, -2, -1, 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11 60 | // rotation values for angle 5.000000 starts at 200 61 | , -13, -11, -10, -8, -7, -6, -4, -3, -1, 0, 1, 3, 4, 6, 7, 8, 10, 11, 13, 14 62 | }; 63 | 64 | 65 | #endif // _ROTATION_H_ 66 | -------------------------------------------------------------------------------- /RotatePy/rotate_2_degrees/res/bg/platform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotatePy/rotate_2_degrees/res/bg/platform.png -------------------------------------------------------------------------------- /RotatePy/rotate_2_degrees/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE platform "bg/platform.png" NONE 2 | PALETTE platform_pal "bg/platform.png" -------------------------------------------------------------------------------- /RotatePy/rotate_2_degrees/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | #include "rotation.h" 4 | 5 | ///////////////////////////////////////////////////////////////////// 6 | // Scrolling Stuff 7 | #define PLANE_MAX_TILE 64 8 | 9 | int main(bool hard) 10 | { 11 | VDP_setScreenWidth320(); 12 | // set colors 13 | PAL_setPalette( PAL0, platform_pal.data, CPU ); 14 | 15 | // set scrolling mode to LINE for horizontal and TILE for vertical 16 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_COLUMN); 17 | 18 | // get tile positions in VRAM. 19 | int ind = TILE_USER_INDEX; 20 | int indexA = ind; 21 | // Load the plane tiles into VRAM 22 | VDP_loadTileSet(platform.tileset, ind, DMA); 23 | 24 | // setup the tiles 25 | VDP_setTileMapEx(BG_A, platform.tilemap, TILE_ATTR_FULL(PAL0, TRUE, FALSE, FALSE, indexA), 26 | 0, // Plane X destination 27 | 0, // plane Y destination 28 | 0, // Region X start position 29 | 0, // Region Y start position 30 | PLANE_MAX_TILE, // width (went with 64 becasue default width is 64. Viewable screen is 40) 31 | 28, // height 32 | CPU); 33 | 34 | // set SGDK scrolling functions to fake the rotaiton. 35 | VDP_setHorizontalScrollLine(BG_A, START_ROW_A, _hScroll, ROWS_A, CPU); 36 | VDP_setVerticalScrollTile(BG_A, START_COL_A, _vScroll, COLS_A, CPU); 37 | 38 | while (TRUE) 39 | { 40 | // let SGDK do its thing 41 | SYS_doVBlankProcess(); 42 | } 43 | return 0; 44 | } -------------------------------------------------------------------------------- /RotatePy/rotate_2_degrees/src/rotation.h: -------------------------------------------------------------------------------- 1 | #ifndef _ROTATION_H_ 2 | #define _ROTATION_H_ 3 | 4 | 5 | #define _SCROLL_COUNT 1 6 | #define ROWS_A 21 7 | #define START_ROW_A 93 8 | #define END_ROW_A 113 9 | #define COLS_A 20 10 | #define START_COL_A 0 11 | #define END_COL_A 19 12 | 13 | 14 | s16 _hScroll[] = { 15 | // rotation values for angle 2.000000 starts at 0 16 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 17 | 18 | 19 | s16 _vScroll[] = { 20 | // rotation values for angle 2.000000 starts at 0 21 | -5, -4, -4, -3, -3, -2, -2, -1, -1, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6 22 | }; 23 | 24 | 25 | #endif // _ROTATION_H_ 26 | -------------------------------------------------------------------------------- /RotatePy/rotation_and_translation/res/bg/platform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotatePy/rotation_and_translation/res/bg/platform.png -------------------------------------------------------------------------------- /RotatePy/rotation_and_translation/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE platform "bg/platform.png" NONE 2 | PALETTE platform_pal "bg/platform.png" -------------------------------------------------------------------------------- /RotatePy/rotation_and_translation/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | #include "rotation.h" 4 | 5 | ///////////////////////////////////////////////////////////////////// 6 | // Scrolling Stuff 7 | #define PLANE_MAX_TILE 64 8 | 9 | 10 | s16 hScrollA[224]; 11 | s16 vScrollA[20]; 12 | 13 | int main(bool hard) 14 | { 15 | memset(hScrollA, 0, sizeof(hScrollA)); 16 | memset(vScrollA, 0, sizeof(vScrollA)); 17 | VDP_setScreenWidth320(); 18 | // set colors 19 | PAL_setPalette( PAL0, platform_pal.data, CPU ); 20 | 21 | // set scrolling mode to LINE for horizontal and TILE for vertical 22 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_COLUMN); 23 | 24 | // get tile positions in VRAM. 25 | int ind = TILE_USER_INDEX; 26 | int indexA = ind; 27 | // Load the plane tiles into VRAM 28 | VDP_loadTileSet(platform.tileset, ind, DMA); 29 | 30 | // setup the tiles 31 | VDP_setTileMapEx(BG_A, platform.tilemap, TILE_ATTR_FULL(PAL0, TRUE, FALSE, FALSE, indexA), 32 | 0, // Plane X destination 33 | 0, // plane Y destination 34 | 0, // Region X start position 35 | 0, // Region Y start position 36 | PLANE_MAX_TILE, // width (went with 64 becasue default width is 64. Viewable screen is 40) 37 | 28, // height 38 | CPU); 39 | 40 | 41 | s16 currAngle = 5; 42 | s16 stepDir = 1; 43 | u8 angleDelay = 0; 44 | 45 | s16 xOffset = 0; 46 | s16 yOffset = 0; 47 | s16 yOffsetDir = 1; 48 | u8 offsetDelay = 0; 49 | 50 | 51 | while (TRUE) 52 | { 53 | 54 | // handle rotation 55 | ++angleDelay; 56 | if (angleDelay % 6 == 0) 57 | { 58 | currAngle += stepDir; 59 | if (currAngle >= _SCROLL_COUNT) 60 | { 61 | stepDir = -1; 62 | currAngle = 10; 63 | } 64 | else if (currAngle < 0) 65 | { 66 | stepDir = 1; 67 | currAngle = 0; 68 | } 69 | } 70 | ++offsetDelay; 71 | if( offsetDelay % 3 == 0 ) { 72 | yOffset += yOffsetDir; 73 | if( yOffset > 40) { 74 | yOffsetDir = -1; 75 | }else if( yOffset < 0 ) { 76 | yOffsetDir = 1; 77 | } 78 | if( currAngle < 4) { 79 | xOffset+=1; 80 | } else if ( currAngle > 6) { 81 | xOffset-=1; 82 | } 83 | } 84 | 85 | 86 | // could unroll loops to eliminate some overhead 87 | for(int i=0; i < ROWS_A; ++i ) { 88 | hScrollA[ i ] = _hScroll[ currAngle * ROWS_A + i] + xOffset; 89 | } 90 | for (int i = 0; i < COLS_A; ++i) 91 | { 92 | vScrollA[i] = _vScroll[currAngle * COLS_A + i] + yOffset; 93 | } 94 | 95 | 96 | // set SGDK scrolling functions to fake the rotation. 97 | s16 startHorizontalScroll = START_ROW_A - yOffset; 98 | s16 totalRows = ROWS_A ; 99 | if( startHorizontalScroll < 0 ) { 100 | totalRows = ROWS_A + startHorizontalScroll; 101 | startHorizontalScroll = 0; 102 | } 103 | VDP_setHorizontalScrollLine(BG_A, startHorizontalScroll, hScrollA, totalRows, DMA); 104 | VDP_setVerticalScrollTile(BG_A, START_COL_A, vScrollA, COLS_A, DMA); 105 | 106 | // let SGDK do its thing 107 | SYS_doVBlankProcess(); 108 | } 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /RotatePy/rotation_and_translation/src/rotation.h: -------------------------------------------------------------------------------- 1 | #ifndef _ROTATION_H_ 2 | #define _ROTATION_H_ 3 | 4 | 5 | #define _SCROLL_COUNT 11 6 | #define ROWS_A 61 7 | #define START_ROW_A 73 8 | #define END_ROW_A 133 9 | #define COLS_A 20 10 | #define START_COL_A 0 11 | #define END_COL_A 19 12 | 13 | 14 | s16 _hScroll[] = { 15 | // rotation values for angle -5.000000 starts at 0 16 | 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -3, -3 17 | // rotation values for angle -4.000000 starts at 61 18 | , 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2 19 | // rotation values for angle -3.000000 starts at 122 20 | , 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2 21 | // rotation values for angle -2.000000 starts at 183 22 | , 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 23 | // rotation values for angle -1.000000 starts at 244 24 | , 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1 25 | // rotation values for angle 0.000000 starts at 305 26 | , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 27 | // rotation values for angle 1.000000 starts at 366 28 | , -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 29 | // rotation values for angle 2.000000 starts at 427 30 | , -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 31 | // rotation values for angle 3.000000 starts at 488 32 | , -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2 33 | // rotation values for angle 4.000000 starts at 549 34 | , -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2 35 | // rotation values for angle 5.000000 starts at 610 36 | , -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3}; 37 | 38 | 39 | s16 _vScroll[] = { 40 | // rotation values for angle -5.000000 starts at 0 41 | 13, 11, 10, 8, 7, 6, 4, 3, 1, 0, -1, -3, -4, -6, -7, -8, -10, -11, -13, -14 42 | // rotation values for angle -4.000000 starts at 20 43 | , 10, 9, 8, 7, 6, 4, 3, 2, 1, 0, -1, -2, -3, -4, -6, -7, -8, -9, -10, -11 44 | // rotation values for angle -3.000000 starts at 40 45 | , 8, 7, 6, 5, 4, 3, 3, 2, 1, 0, -1, -2, -3, -3, -4, -5, -6, -7, -8, -8 46 | // rotation values for angle -2.000000 starts at 60 47 | , 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -6 48 | // rotation values for angle -1.000000 starts at 80 49 | , 3, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, -1, -1, -1, -1, -2, -2, -2, -3, -3 50 | // rotation values for angle 0.000000 starts at 100 51 | , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 52 | // rotation values for angle 1.000000 starts at 120 53 | , -3, -2, -2, -2, -1, -1, -1, -1, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3 54 | // rotation values for angle 2.000000 starts at 140 55 | , -5, -4, -4, -3, -3, -2, -2, -1, -1, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6 56 | // rotation values for angle 3.000000 starts at 160 57 | , -8, -7, -6, -5, -4, -3, -3, -2, -1, 0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 8 58 | // rotation values for angle 4.000000 starts at 180 59 | , -10, -9, -8, -7, -6, -4, -3, -2, -1, 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11 60 | // rotation values for angle 5.000000 starts at 200 61 | , -13, -11, -10, -8, -7, -6, -4, -3, -1, 0, 1, 3, 4, 6, 7, 8, 10, 11, 13, 14 62 | }; 63 | 64 | 65 | #endif // _ROTATION_H_ 66 | -------------------------------------------------------------------------------- /RotatePy/sgdk_scroll_rotate.py: -------------------------------------------------------------------------------- 1 | 2 | #!/usr/bin/env python 3 | # 4 | 5 | import os, argparse, logging, csv 6 | import math 7 | import numpy as np 8 | from jinja2 import Template 9 | import shutil 10 | from pathlib import Path 11 | from PIL import Image 12 | 13 | 14 | def makeProjectFiles(destDir, rowStart, rowEnd, totalRows, colStart, colEnd, totalCols, centerY, prefixStr, backgroundName, spriteName, targetList, outputFilename ): 15 | # make resource dir and rescomp file 16 | srcFolder = destDir + "/src" 17 | if not os.path.exists(srcFolder): 18 | os.makedirs(srcFolder) 19 | resFolder = destDir + "/res" 20 | if not os.path.exists(resFolder): 21 | os.makedirs(resFolder) 22 | bgFolder = resFolder + "/bg" 23 | if not os.path.exists(bgFolder): 24 | os.makedirs(bgFolder) 25 | spritesFolder = resFolder + "/sprites" 26 | if not os.path.exists(spritesFolder): 27 | os.makedirs(spritesFolder) 28 | 29 | # Copy files to resource folders 30 | shutil.copy( backgroundName + str(".png"), bgFolder ) 31 | shutil.copy( spriteName + str(".png"), spritesFolder ) 32 | shutil.copy( outputFilename, srcFolder + "/" + outputFilename ) 33 | 34 | # Make resources file from template 35 | with open('resources.res.jinja') as resFile: 36 | resTemp = Template( resFile.read() ) 37 | with open( resFolder + "/resources.res", 'w') as outRes: 38 | outRes.write( resTemp.render( 39 | bg_name = backgroundName, 40 | sprite_name = spriteName 41 | )) 42 | 43 | # Make main.c file from template 44 | with open('main.c.jinja') as srcFile: 45 | srcTemp = Template( srcFile.read() ) 46 | with open( srcFolder + "/main.c", 'w') as outSrc: 47 | outSrc.write( srcTemp.render( 48 | rotation_filename = outputFilename, 49 | start_row_a = rowStart, 50 | end_row_a = rowEnd, 51 | rows_a = totalRows, 52 | start_col_a = colStart, 53 | end_col_a = colEnd, 54 | cols_a = totalCols, 55 | prefix = prefixStr, 56 | bg_name = backgroundName, 57 | sprite_name = spriteName, 58 | target_list = targetList 59 | )) 60 | 61 | 62 | def main(args, loglevel): 63 | logging.basicConfig(format="%(levelname)s: %(message)s", level=loglevel) 64 | 65 | logging.debug("Arguments:") 66 | logging.debug(args) 67 | 68 | minRot = args.start_angle 69 | maxRot = args.end_angle 70 | rotStep = args.angle_increment 71 | if minRot > maxRot: 72 | print("Starting angle is larger than end angle") 73 | return 74 | if rotStep <= 0.0: 75 | print("Invalid angle increment") 76 | return 77 | 78 | # default to center 18 columns 79 | colStart = args.column_start 80 | colEnd = args.column_end 81 | totalCols = colEnd - colStart +1 # inclusive total+1 82 | 83 | centerX = args.center_x 84 | 85 | # check background image size if not specified and we're making a project dir 86 | projectDir = args.project_directory 87 | backgroundFilename = args.background_filename 88 | backgroundName = '' 89 | if os.path.isfile( backgroundFilename ): 90 | backgroundName = Path( backgroundFilename ).stem 91 | else: 92 | print('Unable to find ' + backgroundFilename) 93 | return 94 | 95 | if args.image_width: 96 | imageWidth = args.image_width 97 | else: 98 | # if we're creating a project, get the width from the image 99 | if len(projectDir) > 0 and len(backgroundFilename) > 0 : 100 | im = Image.open( backgroundFilename ) 101 | imWidth, imHeight = im.size 102 | if( imWidth > 0 ): 103 | imageWidth = imWidth 104 | else: 105 | # if we're not, assume 320 106 | imageWidth = 320 107 | 108 | 109 | 110 | imageShift = -( imageWidth - 320) / 2; 111 | 112 | # default to all rows. 113 | rowStart = args.row_start 114 | rowEnd = args.row_end 115 | totalRows = rowEnd - rowStart +1 # inclusive total +1 116 | centerY = args.center_y 117 | 118 | # where do we put the file at 119 | outputFilename = args.output_filename 120 | 121 | # naming the variables 122 | prefix = args.prefix 123 | 124 | 125 | # (optional) points to rotate 126 | pointsFilename = args.points_filename 127 | pointsToRotate = { } 128 | isFile = os.path.isfile( pointsFilename ) 129 | if isFile: 130 | with open( pointsFilename ) as csvFile: 131 | csvReader = csv.reader( csvFile, delimiter=',') 132 | for row in csvReader: 133 | if len(row) >= 3 and row[1].strip().isnumeric() and row[2].strip().isnumeric(): 134 | newKey = row[0] 135 | while newKey in pointsToRotate: 136 | newKey = row[0] + str(sn) 137 | sn += 1 138 | pointsToRotate[newKey] = ( float(row[1].strip()), float(row[2].strip()) ) 139 | print('found points to rotate:') 140 | print( pointsToRotate ) 141 | 142 | 143 | spriteFilename = args.sprite_filename 144 | spriteName = '' 145 | if os.path.isfile( spriteFilename ): 146 | spriteName = Path( spriteFilename ).stem 147 | else: 148 | print('Unable to find ' + spriteFilename) 149 | return 150 | 151 | 152 | # Sega specific 153 | COL_WIDTH = 16 # 16 pixel width 154 | ROW_HEIGHT = 1 # rows are 1 pixel in height, might be plausible to do 8 for tiled? 155 | 156 | 157 | logging.info("Parameters") 158 | logging.info("Start angle: %f Stop angle: %f Step size: %f", minRot, maxRot, rotStep) 159 | logging.info("Columns to rotate: %d Center column: %d", totalCols, centerX) 160 | logging.info("Rows to rotate: %d Center row: %d", totalRows, centerY) 161 | 162 | # output C defines for colums and rows in 163 | print("#define ROWS_A %d" % totalRows ) 164 | print("#define START_ROW_A %d" % rowStart ) 165 | print("#define END_ROW_A %d" % rowEnd ) 166 | print("#define COLS_A %d" % totalCols ) 167 | print("#define START_COL_A %d" % colStart ) 168 | print("#define END_COL_A %d" % colEnd ) 169 | 170 | # Check if file exists, exit to force user to make decision to delete isntead of just blowing it away. 171 | isFile = os.path.isfile( outputFilename ) 172 | if isFile: 173 | print("File: %s exists! exiting" % outputFilename) 174 | return 175 | 176 | # open file 177 | outfile = open( outputFilename, 'w') 178 | outfile.write("#ifndef _%s_\n" % outputFilename.upper().replace(".","_") ) 179 | outfile.write("#define _%s_\n" % outputFilename.upper().replace(".","_") ) 180 | outfile.write("\n\n#define %s_SCROLL_COUNT %d\n" % ( prefix.replace(".","_"), int(1+ (maxRot - minRot)/rotStep) )) 181 | outfile.write("#define %s_ROWS_A %d\n" % (prefix, totalRows )) 182 | outfile.write("#define %s_START_ROW_A %d\n" % (prefix, rowStart )) 183 | outfile.write("#define %s_END_ROW_A %d\n" % (prefix, rowEnd )) 184 | outfile.write("#define %s_COLS_A %d\n" % (prefix, totalCols )) 185 | outfile.write("#define %s_START_COL_A %d\n" % (prefix, colStart )) 186 | outfile.write("#define %s_END_COL_A %d\n" % (prefix, colEnd )) 187 | outfile.write("\n\ns16 %s_hScroll[] = {" % (prefix ) ) 188 | # horizontal scrolling values 189 | offset = 0 190 | for deg in np.arange( minRot, maxRot + rotStep, rotStep ): # include end rot 191 | rad = deg * math.pi/180; 192 | outfile.write("\n // rotation values for angle %f starts at %d\n" %( deg, offset) ) 193 | for row in range( rowStart, rowEnd+1, ROW_HEIGHT ): 194 | rowShift = (row-centerY) * math.sin( rad ) + imageShift 195 | logging.debug("HSCROLL deg:%f row: %d scroll value:%d", deg, row, rowShift) 196 | if offset > 0: 197 | outfile.write( ", " ) 198 | outfile.write( str(round(rowShift)) ) 199 | offset +=1 200 | outfile.write("};\n") 201 | 202 | outfile.write("\n\ns16 %svScroll[] = {" % (prefix + "_")) 203 | offset = 0 204 | # vertical scrolling values 205 | for deg in np.arange( minRot, maxRot + rotStep, rotStep ): # include end rot 206 | rad = deg * math.pi/180; 207 | outfile.write("\n // rotation values for angle %f starts at %d\n" % (deg,offset)) 208 | for col in range( colStart, colEnd+1, 1 ): 209 | colShift = 16 * (col - centerX) * math.sin( rad ) 210 | logging.debug("VSCROLL deg:%f col: %d scroll value:%d", deg, col, colShift) 211 | if offset > 0: 212 | outfile.write( ", " ) 213 | outfile.write( str(round(colShift)) ) 214 | offset +=1 215 | outfile.write("\n};\n") 216 | 217 | if len(pointsToRotate) > 0: 218 | # loop over again 219 | for key, val in pointsToRotate.items(): 220 | first = True 221 | outfile.write("\n\ns16 %sX[] = {" % (key)) 222 | for deg in np.arange( minRot, maxRot + rotStep, rotStep ): # include end rot 223 | rad = deg * math.pi/180; 224 | # Using real rotation but Y-flipped due to genesis coordinate system. 225 | newX = centerX* 16 + ( val[0] + imageShift - centerX * 16) * math.cos(rad) - (centerY - val[1]) * math.sin(rad) 226 | if not first: 227 | outfile.write( ", " ) 228 | else: 229 | first = False; 230 | 231 | outfile.write("\n %d" % (round(newX))) 232 | outfile.write("\n};") 233 | 234 | for key, val in pointsToRotate.items(): 235 | first = True 236 | outfile.write("\n\ns16 %sY[] = {" % (key)) 237 | for deg in np.arange( minRot, maxRot + rotStep, rotStep ): # include end rot 238 | rad = deg * math.pi/180; 239 | # find the row current point from y = x sin(theta) + y cos (theta) 240 | newY = centerY - ( ((val[0] + imageShift - centerX * 16) * math.sin(rad) ) + ((centerY - val[1]) * math.cos(rad) ) ) 241 | if not first: 242 | outfile.write( ", " ) 243 | else: 244 | first = False; 245 | 246 | outfile.write("\n %d" % (round(newY))) 247 | outfile.write("\n};") 248 | 249 | outfile.write("\n\n#endif // _%s_\n" % outputFilename.upper().replace(".","_") ) 250 | outfile.close() 251 | 252 | # if defined, create an SGDK project skeleton 253 | if len(projectDir) > 0: 254 | makeProjectFiles(projectDir, rowStart, rowEnd, totalRows, colStart, colEnd, totalCols, centerY, prefix, backgroundName, spriteName, pointsToRotate.keys(), outputFilename ) 255 | 256 | 257 | # Standard boilerplate to call the main() function to begin 258 | # the program. 259 | if __name__ == '__main__': 260 | parser = argparse.ArgumentParser( description = "Generates rotation arrays for SGDK.", 261 | epilog = "As an alternative to the commandline, params can be placed in a file, one per line, and specified on the commandline like '%(prog)s @params.conf'.", 262 | fromfile_prefix_chars = '@' ) 263 | # TODO Specify your real parameters here. 264 | parser.add_argument( "-v", 265 | "--verbose", 266 | help="Print debug messages", 267 | action="store_true") 268 | 269 | parser.add_argument( "-s", 270 | "--start_angle", 271 | default=-5.0, 272 | type=float, 273 | help = "Starting rotation in degrees", 274 | metavar = "ARG") 275 | parser.add_argument( "-e", 276 | "--end_angle", 277 | default=5.0, 278 | type=float, 279 | help = "End rotation angle in degrees", 280 | metavar = "ARG") 281 | parser.add_argument( "-i", 282 | "--angle_increment", 283 | default=1.0, 284 | type=float, 285 | help = "Rotation step size", 286 | metavar = "ARG") 287 | 288 | parser.add_argument( "-c", 289 | "--column_start", 290 | default=0, 291 | type=int, 292 | help = "First column to rotate (default 0)", 293 | metavar = "ARG") 294 | parser.add_argument( "-C", 295 | "--column_end", 296 | default=19, 297 | type=int, 298 | help = "Last column to rotate (default 19)", 299 | metavar = "ARG") 300 | parser.add_argument( "-x", 301 | "--center_x", 302 | default=9, 303 | type=int, 304 | help = "Which column is the center of rotation", 305 | metavar = "ARG") 306 | 307 | parser.add_argument( "-w", 308 | "--image_width", 309 | #default=320, 310 | type=int, 311 | help = "Width of image to rotate", 312 | metavar = "ARG") 313 | 314 | parser.add_argument( "-r", 315 | "--row_start", 316 | default=0, 317 | type=int, 318 | help = "First row to rotate", 319 | metavar = "ARG") 320 | parser.add_argument( "-R", 321 | "--row_end", 322 | default=223, 323 | type=int, 324 | help = "Last row to rotate", 325 | metavar = "ARG") 326 | parser.add_argument( "-y", 327 | "--center_y", 328 | default=112, 329 | type=int, 330 | help = "Which row is the center of rotation", 331 | metavar = "ARG") 332 | 333 | parser.add_argument( "-o", 334 | "--output_filename", 335 | default="rotation.h", 336 | help = "Output filename", 337 | metavar = "ARG") 338 | 339 | parser.add_argument( "-P", 340 | "--prefix", 341 | default="", 342 | help = "Add a prefix to array names", 343 | metavar = "ARG") 344 | 345 | parser.add_argument( "-p", 346 | "--project_directory", 347 | default="", 348 | help = "Create project directory with resource files and simple SGDK code.", 349 | metavar = "ARG") 350 | 351 | parser.add_argument( "-b", 352 | "--background_filename", 353 | default="ship.png", 354 | help = "Specify background image", 355 | metavar = "ARG") 356 | 357 | parser.add_argument( "-t", 358 | "--points_filename", 359 | default="", 360 | help = "Specify CSV file with points to rotate along with bg", 361 | metavar = "ARG") 362 | 363 | parser.add_argument( "-S", 364 | "--sprite_filename", 365 | default="crosshairs.png", 366 | help = "Specify target sprite image", 367 | metavar = "ARG") 368 | 369 | args = parser.parse_args() 370 | 371 | # Setup logging 372 | if args.verbose: 373 | loglevel = logging.DEBUG 374 | else: 375 | loglevel = logging.INFO 376 | 377 | main(args, loglevel) 378 | 379 | 380 | -------------------------------------------------------------------------------- /RotatePy/ship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotatePy/ship.png -------------------------------------------------------------------------------- /Rotation/res/planes/boss_truck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/Rotation/res/planes/boss_truck.png -------------------------------------------------------------------------------- /Rotation/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE bg_boss_truck "planes/boss_truck.png" NONE 2 | 3 | PALETTE boss_truck_pal "planes/boss_truck.png" 4 | -------------------------------------------------------------------------------- /Rotation/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | 4 | 5 | ///////////////////////////////////////////////////////////////////// 6 | // Joypad Handler 7 | u16 maxAngle = 5; 8 | static void readJoypad( u16 joypadId ) { 9 | u16 joypadState = JOY_readJoypad( joypadId ); 10 | if( joypadState & BUTTON_A ) { 11 | maxAngle = 5; 12 | }else if( joypadState & BUTTON_B ) { 13 | maxAngle = 10; 14 | }else if( joypadState & BUTTON_C ) { 15 | maxAngle = 15; 16 | }else if( joypadState & BUTTON_X ) { 17 | maxAngle = 20; 18 | }else if( joypadState & BUTTON_Y ) { 19 | maxAngle = 30; 20 | }else if( joypadState & BUTTON_Z ) { 21 | maxAngle = 40; 22 | } 23 | } 24 | 25 | 26 | ///////////////////////////////////////////////////////////////////// 27 | // Scrolling Stuff 28 | // 29 | // default SGDK width is 512 pixels (64 tiles) 30 | #define PLANE_MAX_PIXEL 512 31 | #define PLANE_MAX_TILE 64 32 | 33 | #define ROWS_A 200 34 | #define START_ROW_A 24 35 | #define COLS_A 20 36 | #define START_COL_A 0 37 | s16 hScrollA[224]; 38 | s16 vScrollA[20]; 39 | 40 | 41 | 42 | void setAngle( u16 angle, int centerY ) { 43 | // angle is defined as [0..1024] mapped to [0..2PI] range. 44 | // negative rotation will be 1024 down to 512 45 | // each value is ~ 0.35 degrees / 0.0061 radians. 46 | //KLog_F2x( 4, "c: ", cosFix32(angle), " s: ", sinFix32(angle)); 47 | 48 | for( int row = START_ROW_A; row < START_ROW_A + ROWS_A; ++row ){ 49 | fix32 shift = fix32Mul(FIX32( (row - centerY) ), sinFix32(angle)); 50 | // KLog_S2( " row: ", row, " off: ", (row - centerY)); 51 | //KLog_F1x( 4, " shift: ", shift ); 52 | hScrollA[row] = fix32ToInt( shift ) - 24; 53 | } 54 | 55 | 56 | 57 | // vertical scroll tiles are 16 pixels wide. Using 8 * (col-10) to scale the scrolling effect 58 | // at the extreme left and right of the screen the factor would be -80 and + 80 59 | for( int col = START_COL_A; col < START_COL_A + COLS_A; ++col ){ 60 | fix32 shift = fix32Mul(FIX32( 16 * (col - 10) ), sinFix32(angle)); 61 | vScrollA[col] = fix32ToInt( shift ); 62 | } 63 | 64 | 65 | } 66 | 67 | 68 | int main(bool hard) 69 | { 70 | VDP_setScreenWidth320(); 71 | // set colors 72 | PAL_setPalette( PAL0, boss_truck_pal.data, CPU ); 73 | 74 | memset( hScrollA, 0, sizeof(hScrollA)); 75 | memset( vScrollA, 0, sizeof(vScrollA)); 76 | 77 | // set scrolling mode to LINE for horizontal and TILE for vertical 78 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_COLUMN); 79 | 80 | // get tile positions in VRAM. 81 | int ind = TILE_USER_INDEX; 82 | int indexA = ind; 83 | // Load the plane tiles into VRAM 84 | VDP_loadTileSet(bg_boss_truck.tileset, ind, DMA); 85 | 86 | // setup the tiles 87 | VDP_setTileMapEx(BG_A, bg_boss_truck.tilemap, TILE_ATTR_FULL(PAL0, TRUE, FALSE, FALSE, indexA), 88 | 0, // Plane X destination 89 | 0, // plane Y destination 90 | 0, // Region X start position 91 | 0, // Region Y start position 92 | PLANE_MAX_TILE, // width (went with 64 becasue default width is 64. Viewable screen is 40) 93 | 28, // height 94 | CPU); 95 | 96 | 97 | 98 | u16 currAngle = 0; 99 | setAngle(currAngle, 150); 100 | int stepDir = 1; 101 | while (TRUE) 102 | { 103 | // read joypad to set max angle dynamically 104 | readJoypad(JOY_1); 105 | 106 | 107 | // handle rotation 108 | currAngle += stepDir; 109 | if( stepDir ==1 && currAngle <512 ) { 110 | if( currAngle > maxAngle) { 111 | stepDir = -1; 112 | } 113 | } else if ( stepDir == -1 && currAngle == 0 ) { 114 | currAngle =1024; 115 | } else if ( stepDir == -1 && currAngle > 512 ) { 116 | if( currAngle < 1024-maxAngle) { 117 | stepDir = 1; 118 | } 119 | } else if ( stepDir == 1 && currAngle > 1024 ) { 120 | currAngle =0; 121 | } 122 | setAngle(currAngle, 130); 123 | 124 | 125 | 126 | // set scrolling to fake the rotaiton. 127 | VDP_setHorizontalScrollLine(BG_A, START_ROW_A, hScrollA, ROWS_A, DMA); 128 | VDP_setVerticalScrollTile(BG_A, START_COL_A, vScrollA, COLS_A, DMA); 129 | 130 | 131 | // let SGDK do its thing 132 | SYS_doVBlankProcess(); 133 | } 134 | return 0; 135 | } 136 | -------------------------------------------------------------------------------- /RotationDual/res/bg/planea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotationDual/res/bg/planea.png -------------------------------------------------------------------------------- /RotationDual/res/bg/planeb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotationDual/res/bg/planeb.png -------------------------------------------------------------------------------- /RotationDual/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE plane_a "bg/planea.png" 0 2 | IMAGE plane_b "bg/planeb.png" 0 3 | 4 | SPRITE ships "sprites/ship_sheet.png" 4 4 NONE 5 | SPRITE shots "sprites/shot_sheet.png" 1 1 NONE 1 6 | 7 | PALETTE plane_a_pal "bg/planea.png" 8 | PALETTE plane_b_pal "bg/planeb.png" 9 | PALETTE ships_pal "sprites/ship_sheet.png" 10 | PALETTE shots_pal "sprites/shot_sheet.png" 11 | -------------------------------------------------------------------------------- /RotationDual/res/sprites/ship_sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotationDual/res/sprites/ship_sheet.png -------------------------------------------------------------------------------- /RotationDual/res/sprites/shot_sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotationDual/res/sprites/shot_sheet.png -------------------------------------------------------------------------------- /RotationDual/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | 4 | 5 | ///////////////////////////////////////////////////////////////////// 6 | // Scrolling 7 | // 8 | // default SGDK width is 512 pixels (64 tiles) 9 | #define PLANE_MAX_PIXEL 384 10 | #define PLANE_MAX_HORIZONTAL_TILE 48 11 | #define PLANE_MAX_VERTICAL_TILE 32 12 | s16 hScrollA[224]; 13 | s16 vScrollA[20] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 14 | s16 vScrollUpperA[20]; 15 | s16 vScrollLowerA[20]; 16 | s16 planeADeltas[20] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 17 | 18 | s16 vScrollB[20] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 19 | s16 planeBDeltas[20] = {9, 9, 7, 7, 5, 5, 5, 4, 3, 2, 2, 3, 4, 5, 5, 5, 7, 7, 9, 9}; 20 | //s16 planeBDeltas[20] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 21 | 22 | // setup fake rotation by changing the foreground horizontal and vertical scrolling 23 | void setAngle( u16 angle, s16 startY, s16 endY, s16 centerY, s16 vscroll[], s16 hOffset, s16 vOffset ) { 24 | for( int row = startY; row < endY; ++row ){ 25 | fix32 shift = fix32Mul(FIX32( (row - centerY)>>1 ), sinFix32(angle)); 26 | hScrollA[row] = fix32ToInt( shift ) - 32 + hOffset; 27 | } 28 | 29 | // vertical scroll tiles are 16 pixels wide. Using 8 * (col-10) to scale the scrolling effect 30 | // at the extreme left and right of the screen the factor would be -80 and + 80 31 | for( int col = 1; col < 19; ++col ){ 32 | fix32 shift = fix32Mul(FIX32( (col - 9)<<3 ), sinFix32(angle)); 33 | vscroll[col] = fix32ToInt( shift ) + vOffset; 34 | } 35 | } 36 | 37 | 38 | 39 | 40 | ///////////////////////////////////////////////////////////////////// 41 | // Interrupt handlers 42 | // 43 | static vu16 lineDisplay = 0; // line position on display screen 44 | 45 | HINTERRUPT_CALLBACK HIntHandler() 46 | { 47 | if( lineDisplay == 104 ) { 48 | // set vertical rotation component for lwoer part of BG_A 49 | memcpy( vScrollA, vScrollLowerA, sizeof(vScrollLowerA)); 50 | VDP_setVerticalScrollTile(BG_A, 0, vScrollA, 20, DMA); 51 | } 52 | // Count raster lines 53 | lineDisplay++; 54 | 55 | } 56 | void VBlankHandler() 57 | { 58 | // Reset to line 0 59 | lineDisplay = 0; 60 | 61 | // set vertical rotation component for upper part of BG_A 62 | memcpy( vScrollA, vScrollUpperA, sizeof(vScrollUpperA)); 63 | VDP_setVerticalScrollTile(BG_A, 0, vScrollA, 20, DMA); 64 | } 65 | 66 | 67 | 68 | ///////////////////////////////////////////////////////////////////// 69 | // Player 70 | #define LEFT_EDGE 0 71 | #define RIGHT_EDGE 320 72 | #define TOP_EDGE 0 73 | #define BOTTOM_EDGE 224 74 | #define MAX_SHOTS 3 75 | 76 | int shipAnim = 5; 77 | // sprite info 78 | struct CP_SPRITE 79 | { 80 | Sprite *sprite; 81 | int pos_x; 82 | int pos_y; 83 | int vel_x; 84 | int vel_y; 85 | 86 | int hitbox_x1; 87 | int hitbox_y1; 88 | int hitbox_x2; 89 | int hitbox_y2; 90 | 91 | bool active; 92 | int state; 93 | }; 94 | 95 | struct CP_SPRITE shipSprite; 96 | struct CP_SPRITE shipShots[MAX_SHOTS]; 97 | 98 | 99 | 100 | static void readJoypad( u16 joypadId ) { 101 | u16 state = JOY_readJoypad( joypadId ); 102 | shipSprite.vel_x = 0; 103 | shipSprite.vel_y = 0; 104 | 105 | // Set player velocity if left or right are pressed; 106 | // set velocity to 0 if no direction is pressed 107 | if (state & BUTTON_RIGHT) 108 | { 109 | shipSprite.vel_x = 2; 110 | } 111 | else if (state & BUTTON_LEFT) 112 | { 113 | shipSprite.vel_x = -2; 114 | } 115 | 116 | if (state & BUTTON_UP) 117 | { 118 | shipSprite.vel_y = -2; 119 | } 120 | else if (state & BUTTON_DOWN) 121 | { 122 | shipSprite.vel_y = 2; 123 | } 124 | } 125 | 126 | void update() 127 | { 128 | // Check horizontal bounds 129 | if (shipSprite.pos_x < LEFT_EDGE) 130 | { 131 | shipSprite.pos_x = LEFT_EDGE; 132 | shipSprite.vel_x = -shipSprite.vel_x; 133 | } 134 | else if (shipSprite.pos_x + (shipSprite.hitbox_x2 - shipSprite.hitbox_x1) > RIGHT_EDGE) 135 | { 136 | shipSprite.pos_x = RIGHT_EDGE - (shipSprite.hitbox_x2 - shipSprite.hitbox_x1); 137 | shipSprite.vel_x = -shipSprite.vel_x; 138 | } 139 | 140 | // Check vertical bounds 141 | if (shipSprite.pos_y < TOP_EDGE) 142 | { 143 | shipSprite.pos_y = TOP_EDGE; 144 | shipSprite.vel_y = -shipSprite.vel_y; 145 | } 146 | else if (shipSprite.pos_y + (shipSprite.hitbox_y2 - shipSprite.hitbox_y1) > BOTTOM_EDGE) 147 | { 148 | shipSprite.pos_y = BOTTOM_EDGE - (shipSprite.hitbox_y2 - shipSprite.hitbox_y1); 149 | shipSprite.vel_y = -shipSprite.vel_y; 150 | } 151 | 152 | // Position the ship 153 | shipSprite.pos_x += shipSprite.vel_x; 154 | shipSprite.pos_y += shipSprite.vel_y; 155 | 156 | } 157 | 158 | 159 | int main(bool hard) 160 | { 161 | 162 | // SETUP backgroupd 163 | VDP_setBackgroundColor(16); 164 | VDP_setScreenWidth320(); 165 | 166 | // Set Colors 167 | PAL_setPalette(PAL0, plane_b_pal.data, CPU); 168 | PAL_setPalette(PAL1, plane_a_pal.data, CPU); 169 | PAL_setPalette(PAL2, ships_pal.data, CPU); 170 | PAL_setPalette(PAL3, shots_pal.data, CPU); 171 | 172 | PAL_setColor(0, 0x0000); 173 | 174 | // set scrolling modes to support fake rotation 175 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_COLUMN); 176 | 177 | // get initial tile position in VRAM 178 | int ind = TILE_USER_INDEX; 179 | int indexA = ind; 180 | VDP_loadTileSet(plane_a.tileset, indexA, DMA); 181 | int indexB = ind + plane_b.tileset->numTile; // AND get next position in VRAM ; 182 | VDP_loadTileSet(plane_b.tileset, indexB, DMA); 183 | 184 | // SImple image for BG_B. We're not changing it during the level 185 | VDP_drawImageEx(BG_B, &plane_b, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, indexB), 0, 0, FALSE, TRUE); 186 | 187 | // Setup BG_A 188 | VDP_setTileMapEx(BG_A, plane_a.tilemap, TILE_ATTR_FULL(PAL1, TRUE, FALSE, FALSE, indexA), 189 | 0, // Plane X destination 190 | 0, // plane Y destination 191 | 0, // Region X start position 192 | 0, // Region Y start position 193 | PLANE_MAX_HORIZONTAL_TILE, // 194 | PLANE_MAX_VERTICAL_TILE, // 195 | CPU); 196 | 197 | 198 | // setup sprite 199 | SPR_init(); 200 | 201 | shipSprite.pos_x = 144; 202 | shipSprite.pos_y = 160; 203 | shipSprite.vel_x = 0; 204 | shipSprite.vel_y = 0; 205 | shipSprite.active = TRUE; 206 | shipSprite.hitbox_x1 = 0; 207 | shipSprite.hitbox_y1 = 0; 208 | shipSprite.hitbox_x2 = 32; 209 | shipSprite.hitbox_y2 = 32; 210 | shipAnim = 5; 211 | shipSprite.sprite = SPR_addSprite(&ships, shipSprite.pos_x, shipSprite.pos_y, TILE_ATTR(PAL2, 1, FALSE, FALSE)); 212 | SPR_setAnim(shipSprite.sprite, shipAnim); 213 | 214 | SPR_update(); 215 | 216 | JOY_init(); 217 | 218 | 219 | for (int row = 0; row < 224; ++row) 220 | { 221 | hScrollA[row] = -40; 222 | } 223 | 224 | // Setup interrupt handlers 225 | SYS_disableInts(); 226 | { 227 | SYS_setVBlankCallback(VBlankHandler); 228 | SYS_setHIntCallback(HIntHandler); 229 | VDP_setHIntCounter(0); 230 | VDP_setHInterrupt(1); 231 | } 232 | SYS_enableInts(); 233 | 234 | 235 | // pre calc angles to simplify logic later 236 | u16 upperAngles[80]; 237 | u16 upperAnglePos = 0; 238 | for( s16 angle = 20; angle > 0; --angle) { 239 | upperAngles[upperAnglePos] = angle; 240 | upperAngles[79-upperAnglePos] = angle; 241 | ++upperAnglePos; 242 | } 243 | for( s16 angle = 1023; angle > 1003; --angle) { 244 | upperAngles[upperAnglePos] = angle; 245 | upperAngles[79-upperAnglePos] = angle; 246 | ++upperAnglePos; 247 | } 248 | upperAnglePos = 0; 249 | 250 | u16 lowerAngles[60]; 251 | u16 lowerAnglePos = 0; 252 | for( s16 angle = 15; angle > 0; --angle) { 253 | lowerAngles[lowerAnglePos] = angle; 254 | lowerAngles[59-lowerAnglePos] = angle; 255 | ++lowerAnglePos; 256 | } 257 | for( s16 angle = 1023; angle > 1008; --angle) { 258 | lowerAngles[lowerAnglePos] = angle; 259 | lowerAngles[59-lowerAnglePos] = angle; 260 | ++lowerAnglePos; 261 | } 262 | for( int i=0; i < 60; ++i ) { 263 | KLog_U1("angle: ", lowerAngles[i] ); 264 | } 265 | lowerAnglePos = 30; 266 | 267 | s16 upperHShift = 0; 268 | s16 upperVShift = -15; 269 | s16 upperVShiftMax = 5; 270 | s16 upperVShiftMin = -15; 271 | s16 upperVShiftDir = 1; 272 | 273 | s16 lowerVShift = -16; 274 | s16 lowerVShiftMax = 0; 275 | s16 lowerVShiftMin = -16; 276 | s16 lowerVShiftDir = 1; 277 | 278 | u16 delay = 0; 279 | for( s16 angle = 30; angle >= 0; --angle ) { 280 | setAngle(angle, 0, 90, 40, vScrollUpperA, 0, 0 ); 281 | KLog("-----------------------"); 282 | KLog_U1("angle: ", angle ); 283 | for( s16 row = 0; row < 90; ++row ) { 284 | KLog_S2("row:", row, " hScroll:", hScrollA[row]); 285 | } 286 | for( s16 col = 0; col < 20; ++col ) { 287 | KLog_S2("col:", col, " vScroll:", vScrollUpperA[col]); 288 | } 289 | } 290 | 291 | for( u16 angle = 1023; angle >= 994; --angle ) { 292 | setAngle(angle, 0, 90, 40, vScrollUpperA, 0, 0 ); 293 | KLog("-----------------------"); 294 | KLog_U1("angle: ", angle ); 295 | for( s16 row = 0; row < 90; ++row ) { 296 | KLog_S2("row:", row, " hScroll:", hScrollA[row]); 297 | } 298 | for( s16 col = 0; col < 20; ++col ) { 299 | KLog_S2("col:", col, " vScroll:", vScrollUpperA[col]); 300 | } 301 | } 302 | 303 | 304 | while (TRUE) 305 | { 306 | if (delay !=1 ) 307 | { 308 | s16 angle = upperAngles[upperAnglePos]; 309 | if( angle < 1019 && angle > 1003 ) { 310 | ++upperHShift; 311 | } else if( angle > 5 && angle < 21 ) { 312 | --upperHShift; 313 | } 314 | 315 | setAngle(angle, 0, 90, 40+upperVShift, vScrollUpperA, upperHShift, upperVShift ); 316 | ++upperAnglePos; 317 | if (upperAnglePos == 80) 318 | { 319 | upperAnglePos = 0; 320 | } 321 | } 322 | else 323 | { 324 | setAngle(lowerAngles[lowerAnglePos], 144, 224, 200, vScrollLowerA, 0, lowerVShift); 325 | //setAngle(0, 144, 224, 200, vScrollLowerA, 0, lowerVShift); 326 | ++lowerAnglePos; 327 | if (lowerAnglePos == 60) 328 | { 329 | lowerAnglePos = 0; 330 | } 331 | lowerVShift += lowerVShiftDir; 332 | if (lowerVShiftDir > 0 && lowerVShift >= lowerVShiftMax) 333 | { 334 | lowerVShiftDir = -1; 335 | } 336 | else if (lowerVShiftDir < 0 && lowerVShift <= lowerVShiftMin) 337 | { 338 | lowerVShiftDir = +1; 339 | } 340 | 341 | // move the top 342 | upperVShift += upperVShiftDir; 343 | if (upperVShiftDir > 0 && upperVShift >= upperVShiftMax) 344 | { 345 | upperVShiftDir = -1; 346 | } 347 | else if (upperVShiftDir < 0 && upperVShift <= upperVShiftMin) 348 | { 349 | upperVShiftDir = +1; 350 | } 351 | } 352 | ++delay; 353 | if (delay > 3) 354 | { 355 | delay = 0; 356 | } 357 | 358 | // scroll the asteroids in BG_B 359 | for (int i = 0; i < 20; i++) 360 | { 361 | vScrollB[i] -= planeBDeltas[i]; 362 | } 363 | 364 | VDP_setHorizontalScrollLine(BG_A, 0, hScrollA, 224, DMA); 365 | VDP_setVerticalScrollTile(BG_B, 0, vScrollB, 20, DMA); // use array to set plane offsets 366 | 367 | // player object 368 | readJoypad(JOY_1); 369 | update(); 370 | 371 | SPR_setPosition(shipSprite.sprite, shipSprite.pos_x, shipSprite.pos_y); 372 | SPR_update(); 373 | 374 | SYS_doVBlankProcess(); 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /RotationDualPrecalc/res/bg/planea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotationDualPrecalc/res/bg/planea.png -------------------------------------------------------------------------------- /RotationDualPrecalc/res/bg/planeb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotationDualPrecalc/res/bg/planeb.png -------------------------------------------------------------------------------- /RotationDualPrecalc/res/resources.res: -------------------------------------------------------------------------------- 1 | 2 | IMAGE planea "bg/planea.png" NONE 3 | IMAGE planeb "bg/planeb.png" NONE 4 | 5 | SPRITE ship "sprites/ship.png" 4 4 NONE 6 | SPRITE crosshairs "sprites/crosshairs.png" 2 2 NONE 7 | 8 | PALETTE planea_pal "bg/planea.png" 9 | PALETTE planeb_pal "bg/planeb.png" 10 | PALETTE ship_pal "sprites/ship.png" 11 | PALETTE crosshairs_pal "sprites/crosshairs.png" 12 | -------------------------------------------------------------------------------- /RotationDualPrecalc/res/sprites/crosshairs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotationDualPrecalc/res/sprites/crosshairs.png -------------------------------------------------------------------------------- /RotationDualPrecalc/res/sprites/ship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotationDualPrecalc/res/sprites/ship.png -------------------------------------------------------------------------------- /RotationDualPrecalc/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | #include "rotation.h" 4 | 5 | ///////////////////////////////////////////////////////////////////// 6 | // Scrolling Stuff 7 | #define PLANE_MAX_TILE 64 8 | 9 | s16 hScrollA[224]; 10 | s16 vScrollA[20]; 11 | s16 vScrollUpperA[20]; 12 | s16 vScrollLowerA[20]; 13 | 14 | s16 vScrollB[20] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 15 | s16 planeBDeltas[20] = {9, 9, 7, 7, 5, 5, 5, 4, 3, 2, 2, 3, 4, 5, 5, 5, 7, 7, 9, 9}; 16 | 17 | ///////////////////////////////////////////////////////////////////// 18 | // Player 19 | typedef struct { 20 | Sprite *sprite; 21 | int pos_x; 22 | int pos_y; 23 | int vel_x; 24 | int vel_y; 25 | 26 | int hitbox_x1; 27 | int hitbox_y1; 28 | int hitbox_x2; 29 | int hitbox_y2; 30 | 31 | bool active; 32 | int state; 33 | 34 | 35 | } CP_SPRITE; 36 | 37 | #define PLAYER_FRAME_COUNT 16 38 | #define PLAYER_WIDTH 32 39 | #define PLAYER_HEIGHT 32 40 | 41 | #define LEFT_EDGE 0 42 | #define RIGHT_EDGE 320 43 | #define TOP_EDGE 0 44 | #define BOTTOM_EDGE 224 45 | #define MAX_SHOTS 3 46 | 47 | 48 | 49 | CP_SPRITE shipSprite; 50 | int shipSpriteAnim = 5; 51 | bool doPlayerUpdate; 52 | 53 | 54 | ///////////////////////////////////////////////////////////////////// 55 | // Interrupt handlers 56 | // 57 | static vu16 lineDisplay = 0; // line position on display screen 58 | 59 | HINTERRUPT_CALLBACK HIntHandler() 60 | { 61 | if( lineDisplay == 120 ) { 62 | // set vertical rotation component for lwoer part of BG_A 63 | memcpy( vScrollA, vScrollLowerA, sizeof(vScrollLowerA)); 64 | VDP_setVerticalScrollTile(BG_A, 0, vScrollA, 20, DMA); 65 | } 66 | // Count raster lines 67 | lineDisplay++; 68 | 69 | } 70 | void VBlankHandler() 71 | { 72 | // Reset to line 0 73 | lineDisplay = 0; 74 | 75 | // set vertical rotation component for upper part of BG_A 76 | memcpy( vScrollA, vScrollUpperA, sizeof(vScrollUpperA)); 77 | VDP_setVerticalScrollTile(BG_A, 0, vScrollA, 20, DMA); 78 | } 79 | 80 | 81 | 82 | static void readJoypad( u16 joypadId ) { 83 | u16 state = JOY_readJoypad( joypadId ); 84 | shipSprite.vel_x = 0; 85 | shipSprite.vel_y = 0; 86 | 87 | // Set shipSprite velocity if left or right are pressed; 88 | // set velocity to 0 if no direction is pressed 89 | if (state & BUTTON_RIGHT) 90 | { 91 | shipSprite.vel_x = 2; 92 | } 93 | else if (state & BUTTON_LEFT) 94 | { 95 | shipSprite.vel_x = -2; 96 | } 97 | 98 | if (state & BUTTON_UP) 99 | { 100 | shipSprite.vel_y = -2; 101 | } 102 | else if (state & BUTTON_DOWN) 103 | { 104 | shipSprite.vel_y = 2; 105 | } 106 | } 107 | 108 | 109 | void update() 110 | { 111 | // Check horizontal bounds 112 | if (shipSprite.pos_x < LEFT_EDGE) 113 | { 114 | shipSprite.pos_x = LEFT_EDGE; 115 | shipSprite.vel_x = -shipSprite.vel_x; 116 | } 117 | else if (shipSprite.pos_x + (shipSprite.hitbox_x2 - shipSprite.hitbox_x1) > RIGHT_EDGE) 118 | { 119 | shipSprite.pos_x = RIGHT_EDGE - (shipSprite.hitbox_x2 - shipSprite.hitbox_x1); 120 | shipSprite.vel_x = -shipSprite.vel_x; 121 | } 122 | 123 | // Check vertical bounds 124 | if (shipSprite.pos_y < TOP_EDGE) 125 | { 126 | shipSprite.pos_y = TOP_EDGE; 127 | shipSprite.vel_y = -shipSprite.vel_y; 128 | } 129 | else if (shipSprite.pos_y + (shipSprite.hitbox_y2 - shipSprite.hitbox_y1) > BOTTOM_EDGE) 130 | { 131 | shipSprite.pos_y = BOTTOM_EDGE - (shipSprite.hitbox_y2 - shipSprite.hitbox_y1); 132 | shipSprite.vel_y = -shipSprite.vel_y; 133 | } 134 | 135 | // Position the ship 136 | shipSprite.pos_x += shipSprite.vel_x; 137 | shipSprite.pos_y += shipSprite.vel_y; 138 | 139 | } 140 | 141 | 142 | int main(bool hard) 143 | { 144 | VDP_setScreenWidth320(); 145 | // set colors 146 | PAL_setPalette( PAL0, planea_pal.data, CPU ); 147 | PAL_setPalette( PAL1, planeb_pal.data, CPU ); 148 | PAL_setPalette( PAL2, ship_pal.data, CPU ); 149 | PAL_setPalette( PAL3, crosshairs_pal.data, CPU ); 150 | 151 | // set scrolling mode to LINE for horizontal and TILE for vertical 152 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_COLUMN); 153 | 154 | // get tile positions in VRAM. 155 | int ind = TILE_USER_INDEX; 156 | int indexA = ind; 157 | // Load the plane tiles into VRAM 158 | VDP_loadTileSet(planea.tileset, ind, DMA); 159 | 160 | int indexB = ind + planeb.tileset->numTile; // AND get next position in VRAM ; 161 | VDP_loadTileSet(planeb.tileset, indexB, DMA); 162 | 163 | // Simple image for BG_B, so just draw it. 164 | VDP_drawImageEx(BG_B, &planeb, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, indexB), 0, 0, FALSE, TRUE); 165 | 166 | 167 | // setup the tiles 168 | VDP_setTileMapEx(BG_A, planea.tilemap, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, indexA), 169 | 0, // Plane X destination 170 | 0, // plane Y destination 171 | 0, // Region X start position 172 | 0, // Region Y start position 173 | PLANE_MAX_TILE, // width (went with 64 becasue default width is 64. Viewable screen is 40) 174 | 28, // height 175 | CPU); 176 | 177 | 178 | u8 ticks = 0; 179 | 180 | s16 currUpperAngle = 0; 181 | s16 upperStepDir = 1; 182 | s16 xUpperOffset = 0; 183 | s16 yUpperOffset = 0; 184 | s16 yUpperOffsetDir = 1; 185 | 186 | s16 currLowerAngle = 0; 187 | s16 lowerStepDir = 1; 188 | s16 xLowerOffset = 0; 189 | s16 yLowerOffset = -10; 190 | s16 yLowerOffsetDir = 1; 191 | 192 | 193 | for( int row = 0; row < 224; ++row ) { 194 | hScrollA[row] = -40; 195 | } 196 | 197 | 198 | // Setup interrupt handlers 199 | SYS_disableInts(); 200 | { 201 | SYS_setVBlankCallback(VBlankHandler); 202 | SYS_setHIntCallback(HIntHandler); 203 | VDP_setHIntCounter(0); 204 | VDP_setHInterrupt(1); 205 | } 206 | SYS_enableInts(); 207 | 208 | // SPRITES 209 | SPR_init(); 210 | 211 | Sprite * lgun_sprite = NULL; 212 | int lgun_pos_x = lgun[currUpperAngle*2]-8; 213 | int lgun_pos_y = lgun[currUpperAngle*2+1]-8; 214 | lgun_sprite = SPR_addSprite( &crosshairs, lgun_pos_x, lgun_pos_y, TILE_ATTR( PAL3, 1, FALSE, FALSE )); 215 | 216 | Sprite * mgun_sprite = NULL; 217 | int mgun_pos_x = mgun[currUpperAngle*2]-8; 218 | int mgun_pos_y = mgun[currUpperAngle*2+1]-8; 219 | mgun_sprite = SPR_addSprite( &crosshairs, mgun_pos_x, mgun_pos_y, TILE_ATTR( PAL3, 1, FALSE, FALSE )); 220 | 221 | Sprite * rgun_sprite = NULL; 222 | int rgun_pos_x = rgun[currUpperAngle*2]-8; 223 | int rgun_pos_y = rgun[currUpperAngle*2+1]-8; 224 | rgun_sprite = SPR_addSprite( &crosshairs, rgun_pos_x, rgun_pos_y, TILE_ATTR( PAL3, 1, FALSE, FALSE )); 225 | 226 | Sprite * lvent_sprite = NULL; 227 | int lvent_pos_x = lvent[currUpperAngle*2]-8; 228 | int lvent_pos_y = lvent[currUpperAngle*2+1]-8; 229 | lvent_sprite = SPR_addSprite( &crosshairs, lvent_pos_x, lvent_pos_y, TILE_ATTR( PAL3, 1, FALSE, FALSE )); 230 | 231 | Sprite * rvent_sprite = NULL; 232 | int rvent_pos_x = rvent[currUpperAngle*2]-8; 233 | int rvent_pos_y = rvent[currUpperAngle*2+1]-8; 234 | rvent_sprite = SPR_addSprite( &crosshairs, rvent_pos_x, rvent_pos_y, TILE_ATTR( PAL3, 1, FALSE, FALSE )); 235 | 236 | 237 | 238 | Sprite * larray_sprite = NULL; 239 | int larray_pos_x = larray[currLowerAngle*2]-8; 240 | int larray_pos_y = larray[currLowerAngle*2+1]-8; 241 | larray_sprite = SPR_addSprite( &crosshairs, larray_pos_x, larray_pos_y, TILE_ATTR( PAL3, 1, FALSE, FALSE )); 242 | 243 | Sprite * marray_sprite = NULL; 244 | int marray_pos_x = marray[currLowerAngle*2]-8; 245 | int marray_pos_y = marray[currLowerAngle*2+1]-8; 246 | marray_sprite = SPR_addSprite( &crosshairs, marray_pos_x, marray_pos_y, TILE_ATTR( PAL3, 1, FALSE, FALSE )); 247 | 248 | Sprite * rarray_sprite = NULL; 249 | int rarray_pos_x = rarray[currLowerAngle*2]-8; 250 | int rarray_pos_y = rarray[currLowerAngle*2+1]-8; 251 | rarray_sprite = SPR_addSprite( &crosshairs, rarray_pos_x, rarray_pos_y, TILE_ATTR( PAL3, 1, FALSE, FALSE )); 252 | 253 | shipSprite.pos_x = 144; 254 | shipSprite.pos_y = 160; 255 | shipSprite.vel_x = 0; 256 | shipSprite.vel_y = 0; 257 | shipSprite.active = TRUE; 258 | shipSprite.hitbox_x1 = 2; 259 | shipSprite.hitbox_y1 = 12; 260 | shipSprite.hitbox_x2 = 30; 261 | shipSprite.hitbox_y2 = 26; 262 | 263 | shipSprite.sprite = SPR_addSprite( &ship, shipSprite.pos_x, shipSprite.pos_y, TILE_ATTR( PAL2, 0, FALSE,FALSE )); 264 | SPR_setAnim( shipSprite.sprite, shipSpriteAnim ); 265 | 266 | JOY_init(); 267 | 268 | while (TRUE) 269 | { 270 | 271 | ++ticks; 272 | if( ticks % 6 == 0 ) { 273 | currUpperAngle += upperStepDir; 274 | if( currUpperAngle >= lower_SCROLL_COUNT ) { 275 | upperStepDir = -1; 276 | currUpperAngle = 9; 277 | }else if (currUpperAngle <0 ) { 278 | upperStepDir = 1; 279 | currUpperAngle = 1; 280 | } 281 | } 282 | if( ticks % 3 == 0 ) { 283 | yUpperOffset += yUpperOffsetDir; 284 | if( yUpperOffset > 5) { 285 | yUpperOffsetDir = -1; 286 | }else if( yUpperOffset < -15 ) { 287 | yUpperOffsetDir = 1; 288 | } 289 | if( currUpperAngle < 4) { 290 | xUpperOffset+=2; 291 | } else if ( currUpperAngle > 6) { 292 | xUpperOffset-=2; 293 | } 294 | } 295 | 296 | if( ticks % 9 == 0 ) { 297 | currLowerAngle += lowerStepDir; 298 | if( currLowerAngle >= upper_SCROLL_COUNT ) { 299 | lowerStepDir = -1; 300 | currLowerAngle = 9; 301 | }else if (currLowerAngle < 0 ) { 302 | lowerStepDir = 1; 303 | currLowerAngle = 1; 304 | } 305 | } 306 | if( ticks % 12 == 0 ) { 307 | yLowerOffset += yLowerOffsetDir; 308 | if( yLowerOffset > -10) { 309 | yLowerOffsetDir = -1; 310 | }else if( yLowerOffset < -25 ) { 311 | yLowerOffsetDir = 1; 312 | } 313 | if( currLowerAngle < 4) { 314 | xLowerOffset+=1; 315 | } else if ( currLowerAngle > 6) { 316 | xLowerOffset-=1; 317 | } 318 | } 319 | 320 | 321 | // could unroll loops to eliminate some overhead 322 | s16 startUpperHScroll = upper_START_ROW_A - yUpperOffset; 323 | s16 stopUpperRows = upper_END_ROW_A - yUpperOffset; 324 | if( startUpperHScroll < 0 ) { 325 | stopUpperRows = upper_START_ROW_A + upper_ROWS_A + startUpperHScroll; 326 | startUpperHScroll = 0; 327 | } 328 | for(int i=startUpperHScroll, offset=0; i < stopUpperRows; ++i, ++offset ) { 329 | hScrollA[ i ] = upper_hScroll[ currUpperAngle * upper_ROWS_A + offset] + xUpperOffset; 330 | } 331 | 332 | s16 startLowerHScroll = lower_START_ROW_A - yLowerOffset; 333 | s16 stopLowerHScroll = lower_END_ROW_A - yLowerOffset; 334 | if( stopLowerHScroll > 223 ) { 335 | stopLowerHScroll =223; 336 | } 337 | for(int i=startLowerHScroll, offset=0; i <= stopLowerHScroll; ++i, ++offset ) { 338 | hScrollA[ i ] = lower_hScroll[ currLowerAngle * lower_ROWS_A + offset] + xLowerOffset; 339 | } 340 | 341 | 342 | // 343 | for (int i = 0; i < upper_COLS_A; ++i) 344 | { 345 | vScrollUpperA[i] = upper_vScroll[currUpperAngle * upper_COLS_A + i] + yUpperOffset; 346 | } 347 | for (int i = 0; i < lower_COLS_A; ++i) 348 | { 349 | vScrollLowerA[i] = lower_vScroll[currLowerAngle * lower_COLS_A + i] + yLowerOffset; 350 | } 351 | 352 | 353 | 354 | // set SGDK scrolling functions to fake the rotation. 355 | VDP_setHorizontalScrollLine(BG_A, 0, hScrollA, 224, DMA); 356 | 357 | // scroll the asteroids in BG_B 358 | for (int i = 0; i < 20; i++) 359 | { 360 | vScrollB[i] -= planeBDeltas[i]; 361 | } 362 | 363 | VDP_setVerticalScrollTile(BG_B, 0, vScrollB, 20, DMA); // use array to set plane offsets 364 | 365 | 366 | 367 | 368 | lgun_pos_x = lgun[currUpperAngle * 2]-8 + xUpperOffset; 369 | lgun_pos_y = lgun[currUpperAngle * 2 + 1]-8 - yUpperOffset; 370 | SPR_setPosition(lgun_sprite, lgun_pos_x, lgun_pos_y); 371 | 372 | mgun_pos_x = mgun[currUpperAngle * 2]-8 + xUpperOffset; 373 | mgun_pos_y = mgun[currUpperAngle * 2 + 1]-8 - yUpperOffset; 374 | SPR_setPosition(mgun_sprite, mgun_pos_x, mgun_pos_y); 375 | 376 | rgun_pos_x = rgun[currUpperAngle * 2]-8 + xUpperOffset; 377 | rgun_pos_y = rgun[currUpperAngle * 2 + 1]-8 - yUpperOffset; 378 | SPR_setPosition(rgun_sprite, rgun_pos_x, rgun_pos_y); 379 | 380 | lvent_pos_x = lvent[currUpperAngle * 2]-8 + xUpperOffset; 381 | lvent_pos_y = lvent[currUpperAngle * 2 + 1]-8 - yUpperOffset; 382 | SPR_setPosition(lvent_sprite, lvent_pos_x, lvent_pos_y); 383 | 384 | rvent_pos_x = rvent[currUpperAngle * 2]-8 + xUpperOffset; 385 | rvent_pos_y = rvent[currUpperAngle * 2 + 1]-8 - yUpperOffset; 386 | SPR_setPosition(rvent_sprite, rvent_pos_x, rvent_pos_y); 387 | 388 | 389 | 390 | larray_pos_x = larray[currLowerAngle * 2]-8 + xLowerOffset; 391 | larray_pos_y = larray[currLowerAngle * 2 + 1]-8 - yLowerOffset; 392 | SPR_setPosition(larray_sprite, larray_pos_x, larray_pos_y); 393 | 394 | marray_pos_x = marray[currLowerAngle * 2]-8 + xLowerOffset; 395 | marray_pos_y = marray[currLowerAngle * 2 + 1]-8 - yLowerOffset; 396 | SPR_setPosition(marray_sprite, marray_pos_x, marray_pos_y); 397 | 398 | rarray_pos_x = rarray[currLowerAngle * 2]-8 + xLowerOffset; 399 | rarray_pos_y = rarray[currLowerAngle * 2 + 1]-8 - yLowerOffset; 400 | SPR_setPosition(rarray_sprite, rarray_pos_x, rarray_pos_y); 401 | 402 | 403 | readJoypad(JOY_1); 404 | update(); 405 | SPR_setPosition(shipSprite.sprite, shipSprite.pos_x, shipSprite.pos_y); 406 | SPR_update(); 407 | 408 | 409 | // let SGDK do its thing 410 | SYS_doVBlankProcess(); 411 | } 412 | return 0; 413 | } 414 | -------------------------------------------------------------------------------- /RotationDualPrecalc/src/rotation.h: -------------------------------------------------------------------------------- 1 | #ifndef _ROTATION_H_ 2 | #define _ROTATION_H_ 3 | 4 | #define upper_SCROLL_COUNT 11 5 | #define upper_ROWS_A 91 6 | #define upper_START_ROW_A 0 7 | #define upper_END_ROW_A 90 8 | #define upper_COLS_A 20 9 | #define upper_START_COL_A 0 10 | #define upper_END_COL_A 19 11 | #define lower_SCROLL_COUNT 11 12 | #define lower_ROWS_A 86 13 | #define lower_START_ROW_A 138 14 | #define lower_END_ROW_A 223 15 | #define lower_COLS_A 20 16 | #define lower_START_COL_A 0 17 | #define lower_END_COL_A 19 18 | 19 | s16 upper_hScroll[] = { 20 | // rotation values for angle -5.000000 starts at 0 21 | -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36 22 | // rotation values for angle -4.000000 starts at 91 23 | , -29, -29, -29, -29, -29, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35 24 | // rotation values for angle -3.000000 starts at 182 25 | , -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -35, -35, -35 26 | // rotation values for angle -2.000000 starts at 273 27 | , -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -34, -34, -34, -34, -34, -34, -34, -34 28 | // rotation values for angle -1.000000 starts at 364 29 | , -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33 30 | // rotation values for angle 0.000000 starts at 455 31 | , -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32 32 | // rotation values for angle 1.000000 starts at 546 33 | , -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31 34 | // rotation values for angle 2.000000 starts at 637 35 | , -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -30, -30, -30, -30, -30, -30, -30, -30 36 | // rotation values for angle 3.000000 starts at 728 37 | , -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -29, -29, -29 38 | // rotation values for angle 4.000000 starts at 819 39 | , -35, -35, -35, -35, -35, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29 40 | // rotation values for angle 5.000000 starts at 910 41 | , -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28}; 42 | 43 | 44 | s16 upper_vScroll[] = { 45 | // rotation values for angle -5.000000 starts at 0 46 | 13, 11, 10, 8, 7, 6, 4, 3, 1, 0, -1, -3, -4, -6, -7, -8, -10, -11, -13, -14 47 | // rotation values for angle -4.000000 starts at 20 48 | , 10, 9, 8, 7, 6, 4, 3, 2, 1, 0, -1, -2, -3, -4, -6, -7, -8, -9, -10, -11 49 | // rotation values for angle -3.000000 starts at 40 50 | , 8, 7, 6, 5, 4, 3, 3, 2, 1, 0, -1, -2, -3, -3, -4, -5, -6, -7, -8, -8 51 | // rotation values for angle -2.000000 starts at 60 52 | , 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -6 53 | // rotation values for angle -1.000000 starts at 80 54 | , 3, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, -1, -1, -1, -1, -2, -2, -2, -3, -3 55 | // rotation values for angle 0.000000 starts at 100 56 | , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 57 | // rotation values for angle 1.000000 starts at 120 58 | , -3, -2, -2, -2, -1, -1, -1, -1, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3 59 | // rotation values for angle 2.000000 starts at 140 60 | , -5, -4, -4, -3, -3, -2, -2, -1, -1, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6 61 | // rotation values for angle 3.000000 starts at 160 62 | , -8, -7, -6, -5, -4, -3, -3, -2, -1, 0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 8 63 | // rotation values for angle 4.000000 starts at 180 64 | , -10, -9, -8, -7, -6, -4, -3, -2, -1, 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11 65 | // rotation values for angle 5.000000 starts at 200 66 | , -13, -11, -10, -8, -7, -6, -4, -3, -1, 0, 1, 3, 4, 6, 7, 8, 10, 11, 13, 14 67 | }; 68 | 69 | 70 | 71 | 72 | s16 lgun[] = { 73 | 73, 44, 74 | 73, 45, 75 | 74, 46, 76 | 74, 48, 77 | 74, 49, 78 | 74, 50, 79 | 74, 51, 80 | 74, 52, 81 | 75, 54, 82 | 75, 55, 83 | 75, 56 84 | }; 85 | 86 | s16 mgun[] = { 87 | 161, 31, 88 | 161, 31, 89 | 161, 31, 90 | 160, 31, 91 | 160, 30, 92 | 160, 30, 93 | 160, 30, 94 | 160, 29, 95 | 159, 29, 96 | 159, 29, 97 | 159, 29 98 | }; 99 | 100 | s16 rgun[] = { 101 | 244, 59, 102 | 244, 57, 103 | 244, 55, 104 | 245, 54, 105 | 245, 52, 106 | 245, 50, 107 | 245, 48, 108 | 245, 46, 109 | 245, 45, 110 | 245, 43, 111 | 245, 41 112 | }; 113 | 114 | s16 lvent[] = { 115 | 143, 46, 116 | 144, 46, 117 | 144, 46, 118 | 144, 46, 119 | 144, 46, 120 | 144, 46, 121 | 144, 46, 122 | 144, 46, 123 | 144, 46, 124 | 144, 46, 125 | 145, 46 126 | }; 127 | 128 | s16 rvent[] = { 129 | 174, 49, 130 | 175, 48, 131 | 175, 48, 132 | 175, 47, 133 | 175, 47, 134 | 175, 46, 135 | 175, 45, 136 | 175, 45, 137 | 175, 44, 138 | 175, 44, 139 | 175, 43 140 | }; 141 | 142 | 143 | 144 | s16 lower_hScroll[] = { 145 | // rotation values for angle -2.500000 starts at 0 146 | -29, -29, -29, -29, -29, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33 147 | // rotation values for angle -2.000000 starts at 86 148 | , -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -33, -33, -33, -33, -33, -33, -33, -33, -33 149 | // rotation values for angle -1.500000 starts at 172 150 | , -30, -30, -30, -30, -30, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -33, -33, -33, -33 151 | // rotation values for angle -1.000000 starts at 258 152 | , -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32 153 | // rotation values for angle -0.500000 starts at 344 154 | , -31, -31, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32 155 | // rotation values for angle 0.000000 starts at 430 156 | , -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32 157 | // rotation values for angle 0.500000 starts at 516 158 | , -33, -33, -33, -33, -33, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32 159 | // rotation values for angle 1.000000 starts at 602 160 | , -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32 161 | // rotation values for angle 1.500000 starts at 688 162 | , -34, -34, -34, -34, -34, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -31, -31, -31, -31 163 | // rotation values for angle 2.000000 starts at 774 164 | , -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -31, -31, -31, -31, -31, -31, -31, -31, -31 165 | // rotation values for angle 2.500000 starts at 860 166 | , -35, -35, -35, -35, -35, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31}; 167 | 168 | 169 | s16 lower_vScroll[] = { 170 | // rotation values for angle -2.500000 starts at 0 171 | 6, 6, 5, 4, 3, 3, 2, 1, 1, 0, -1, -1, -2, -3, -3, -4, -5, -6, -6, -7 172 | // rotation values for angle -2.000000 starts at 20 173 | , 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -6 174 | // rotation values for angle -1.500000 starts at 40 175 | , 4, 3, 3, 3, 2, 2, 1, 1, 0, 0, 0, -1, -1, -2, -2, -3, -3, -3, -4, -4 176 | // rotation values for angle -1.000000 starts at 60 177 | , 3, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, -1, -1, -1, -1, -2, -2, -2, -3, -3 178 | // rotation values for angle -0.500000 starts at 80 179 | , 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1 180 | // rotation values for angle 0.000000 starts at 100 181 | , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 182 | // rotation values for angle 0.500000 starts at 120 183 | , -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1 184 | // rotation values for angle 1.000000 starts at 140 185 | , -3, -2, -2, -2, -1, -1, -1, -1, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3 186 | // rotation values for angle 1.500000 starts at 160 187 | , -4, -3, -3, -3, -2, -2, -1, -1, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4 188 | // rotation values for angle 2.000000 starts at 180 189 | , -5, -4, -4, -3, -3, -2, -2, -1, -1, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6 190 | // rotation values for angle 2.500000 starts at 200 191 | , -6, -6, -5, -4, -3, -3, -2, -1, -1, 0, 1, 1, 2, 3, 3, 4, 5, 6, 6, 7 192 | }; 193 | 194 | 195 | 196 | s16 larray[] = { 197 | 146, 211, 198 | 146, 211, 199 | 146, 211, 200 | 146, 211, 201 | 146, 211, 202 | 146, 211, 203 | 146, 211, 204 | 146, 211, 205 | 146, 211, 206 | 146, 211, 207 | 146, 211 208 | }; 209 | 210 | s16 marray[] = { 211 | 160, 212, 212 | 160, 212, 213 | 160, 211, 214 | 160, 211, 215 | 160, 211, 216 | 160, 211, 217 | 160, 211, 218 | 160, 211, 219 | 160, 211, 220 | 160, 210, 221 | 160, 210 222 | }; 223 | 224 | s16 rarray[] = { 225 | 172, 212, 226 | 173, 212, 227 | 173, 212, 228 | 173, 212, 229 | 173, 211, 230 | 173, 211, 231 | 173, 211, 232 | 173, 210, 233 | 173, 210, 234 | 173, 210, 235 | 173, 210 236 | }; 237 | 238 | 239 | 240 | #endif // _ROTATION_H_ 241 | -------------------------------------------------------------------------------- /RotationWithBackground/res/planes/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotationWithBackground/res/planes/background.png -------------------------------------------------------------------------------- /RotationWithBackground/res/planes/boss_truck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotationWithBackground/res/planes/boss_truck.png -------------------------------------------------------------------------------- /RotationWithBackground/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE bg_boss_truck "planes/boss_truck.png" NONE 2 | IMAGE bg "planes/background.png" NONE 3 | 4 | SPRITE boss_truck_wheel "sprites/boss_truck_wheel.png" 8 8 NONE 2 NONE TILE 5 | SPRITE gun_bike_big "sprites/gun_bike_b.png" 16 7 NONE 2 NONE TILE 6 | SPRITE gun_bike_rider_big "sprites/gun_bike_rider_b.png" 7 7 NONE 0 NONE TILE 7 | 8 | SPRITE tree "sprites/tree1.png" 3 9 NONE 9 | 10 | PALETTE bg_pal "planes/background.png" 11 | PALETTE gun_bike_pal "sprites/gun_bike_b.png" 12 | PALETTE gun_bike_rider_pal "sprites/gun_bike_rider_b.png" 13 | PALETTE boss_truck_pal "planes/boss_truck.png" 14 | -------------------------------------------------------------------------------- /RotationWithBackground/res/sprites/boss_truck_wheel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotationWithBackground/res/sprites/boss_truck_wheel.png -------------------------------------------------------------------------------- /RotationWithBackground/res/sprites/gun_bike_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotationWithBackground/res/sprites/gun_bike_b.png -------------------------------------------------------------------------------- /RotationWithBackground/res/sprites/gun_bike_rider_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotationWithBackground/res/sprites/gun_bike_rider_b.png -------------------------------------------------------------------------------- /RotationWithBackground/res/sprites/tree1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/RotationWithBackground/res/sprites/tree1.png -------------------------------------------------------------------------------- /RotationWithBackground/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | 4 | 5 | 6 | 7 | ///////////////////////////////////////////////////////////////////// 8 | // Scrolling Stuff 9 | // 10 | // default SGDK width is 512 pixels (64 tiles) 11 | #define PLANE_MAX_PIXEL 512 12 | #define PLANE_MAX_TILE 64 13 | 14 | // Images in this project are 1280 pixels wide ( tiles) 15 | #define IMAGE_MAX_PIXEL 1280 16 | #define IMAGE_MAX_TILE 160 17 | 18 | #define ROWS_A 150 19 | #define START_ROW_A 74 20 | #define COLS_A 20 21 | #define START_COL_A 0 22 | s16 hScrollA[224]; 23 | s16 vScrollA[20]; 24 | 25 | 26 | #define ROWS_B 25 27 | #define START_ROW_B 3 28 | fix32 speedB[ROWS_B]; 29 | fix32 planeOffsetB[ROWS_B]; 30 | fix32 imageOffsetB[ROWS_B]; 31 | u16 lastSrcColB[ROWS_B]; 32 | u16 lastDstColB[ROWS_B]; 33 | s16 hScrollB[ROWS_B*8]; 34 | 35 | // setup fake rotation by changing the foreground horizontal and vertical scrolling 36 | void setAngle( u16 angle, int centerY ) { 37 | // angle is defined as [0..1024] mapped to [0..2PI] range. 38 | // negative rotation will be 1024 down to 512 39 | // each value is ~ 0.35 degrees / 0.0061 radians. 40 | //KLog_S1("angle:",angle); 41 | //KLog_F2x( 4, " c: ", cosFix32(angle), " s: ", sinFix32(angle)); 42 | 43 | for( int row = START_ROW_A; row < START_ROW_A + ROWS_A; ++row ){ 44 | fix32 shift = fix32Mul(FIX32( (row - centerY)>>1 ), sinFix32(angle)); 45 | // KLog_S2( " row: ", row, " off: ", (row - centerY)); 46 | //KLog_F1x( 4, " shift: ", shift ); 47 | hScrollA[row-START_ROW_A] = fix32ToInt( shift ) - 24; 48 | } 49 | 50 | 51 | // vertical scroll tiles are 16 pixels wide. Using 8 * (col-10) to scale the scrolling effect 52 | // at the extreme left and right of the screen the factor would be -80 and + 80 53 | for( int col = START_COL_A; col < START_COL_A + COLS_A; ++col ){ 54 | fix32 shift = fix32Mul(FIX32( (col - 9)<<3 ), sinFix32(angle)); 55 | vScrollA[col] = fix32ToInt( shift ); 56 | } 57 | 58 | 59 | } 60 | 61 | 62 | 63 | 64 | // handle horizontal parallax scrolling of background image. 65 | void updateScroll(VDPPlane plane, const TileMap *tilemap, int index, 66 | fix32 *speed, 67 | fix32 *planeOffset, 68 | fix32 *imageOffset, 69 | u16 *lastSrcCol, 70 | u16 *lastDstCol, 71 | s16 *scroll, 72 | u16 startRow, 73 | u16 rows) 74 | { 75 | 76 | for (s16 row = 0; row < rows; ++row) 77 | { 78 | // Set the scrolling position of plane per row 79 | planeOffset[row] = fix32Add(planeOffset[row], speed[row]); 80 | if (planeOffset[row] >= FIX32(PLANE_MAX_PIXEL)) // plane in memory is 512 pixels wide 81 | { 82 | planeOffset[row] = FIX32(0); 83 | } 84 | 85 | // keep track of where we are in the image per row 86 | imageOffset[row] = fix32Add(imageOffset[row], speed[row]); 87 | if (imageOffset[row] >= FIX32(IMAGE_MAX_PIXEL)) // bg image is 1280 pixels wide 88 | { 89 | imageOffset[row] = FIX32(0); 90 | } 91 | s16 sPlaneOffset = fix32ToInt(planeOffset[row]); 92 | // check if we need a new tile 93 | if (sPlaneOffset % 8 == 0) 94 | { 95 | // get destination column (in tiles) 96 | s16 dstCol = (sPlaneOffset + PLANE_MAX_PIXEL - 8) / 8; 97 | if (dstCol >= PLANE_MAX_TILE) 98 | { 99 | dstCol -= PLANE_MAX_TILE; // wrap around to the start of the plane 100 | } 101 | 102 | // get source column (in tiles) 103 | s16 sImageOffset = fix32ToInt(imageOffset[row]); 104 | s16 srcCol = (sImageOffset + PLANE_MAX_PIXEL - 8) / 8; 105 | if (srcCol >= IMAGE_MAX_TILE) 106 | { 107 | srcCol -= IMAGE_MAX_TILE; // wrap around to the start of the image 108 | } 109 | // if the current destination column is smaller n 110 | // the last one, the region is *notnuous 111 | // Two or more tile moves may be required. 112 | if (dstCol != lastDstCol[row]) 113 | { 114 | s16 width = dstCol - lastDstCol[row]; 115 | if (width < 0) 116 | { 117 | width += PLANE_MAX_TILE; 118 | } 119 | // just loop for now ( probably inefficient if there are a lot of columns to copy) 120 | s16 tmpDst = lastDstCol[row] + 1; 121 | s16 tmpSrc = lastSrcCol[row] + 1; 122 | for (s16 i = 0; i < width; ++i) 123 | { 124 | if (tmpDst >= PLANE_MAX_TILE) 125 | { 126 | tmpDst = 0; 127 | } 128 | if (tmpSrc >= IMAGE_MAX_TILE) 129 | { 130 | tmpSrc = 0; 131 | } 132 | VDP_setTileMapEx(plane, tilemap, TILE_ATTR_FULL(PAL0, plane == BG_A ? TRUE : FALSE, FALSE, FALSE, index), 133 | tmpDst, // Plane X destination 134 | startRow + row, // plane Y destination 135 | tmpSrc, // Region X start position 136 | startRow + row, // Region Y start position 137 | 1, // width 138 | 1, // height 139 | CPU); 140 | tmpDst++; 141 | tmpSrc++; 142 | } 143 | lastDstCol[row] = tmpDst - 1; 144 | lastSrcCol[row] = tmpSrc - 1; 145 | } 146 | } 147 | // set offset through all 8 lines for the current "tile" 148 | for( int i=0; i < 8; ++i ) { 149 | scroll[row*8 + i] = -sPlaneOffset; 150 | } 151 | 152 | } 153 | } 154 | 155 | 156 | 157 | void setupB() 158 | { 159 | // setup scrolling vals 160 | for (int row = 0; row < ROWS_B; ++row) 161 | { 162 | planeOffsetB[row] = FIX32(0); 163 | imageOffsetB[row] = FIX32(0); 164 | lastSrcColB[row] = PLANE_MAX_TILE - 1; 165 | lastDstColB[row] = PLANE_MAX_TILE - 1; 166 | hScrollB[row] = 0; 167 | } 168 | // 3-4 fastest cloud 169 | speedB[0] = FIX32(6); 170 | speedB[1] = FIX32(6); 171 | // 5 172 | speedB[2] = FIX32(3); 173 | // 6 174 | speedB[3] = FIX32(1.5); 175 | // 7-8 slowest cloud 176 | speedB[4] = FIX32(0.75); 177 | speedB[5] = FIX32(0.75); 178 | // 9-11 the distances 179 | speedB[6] = FIX32(0.075); 180 | speedB[7] = FIX32(0.075); 181 | speedB[8] = FIX32(0.075); 182 | // 12-13 slow land 183 | speedB[9] = FIX32(0.5); 184 | speedB[10] = FIX32(1.0); 185 | // 14 186 | speedB[11] = FIX32(1.5); 187 | speedB[12] = FIX32(2.2); 188 | speedB[13] = FIX32(3.0); 189 | 190 | // 17-19 191 | for (int row = 17; row <= 19; ++row) 192 | { 193 | speedB[row - START_ROW_B] = FIX32(3.5); 194 | } 195 | // 20-27 fastest ground 196 | for (int row = 20; row < 23; ++row) 197 | { 198 | speedB[row - START_ROW_B] = FIX32(4.7); 199 | } 200 | for (int row = 23; row <= 27; ++row) 201 | { 202 | speedB[row - START_ROW_B] = FIX32(7.6); 203 | } 204 | } 205 | 206 | 207 | ///////////////////////////////////////////////////////////////////// 208 | // Joypad Handler 209 | u16 maxAngle = 7; 210 | static void readJoypad( u16 joypadId ) { 211 | u16 joypadState = JOY_readJoypad( joypadId ); 212 | if( joypadState & BUTTON_A ) { 213 | maxAngle = 7; 214 | }else if( joypadState & BUTTON_B ) { 215 | maxAngle = 10; 216 | }else if( joypadState & BUTTON_C ) { 217 | maxAngle = 15; 218 | }else if( joypadState & BUTTON_X ) { 219 | maxAngle = 20; 220 | }else if( joypadState & BUTTON_Y ) { 221 | maxAngle = 30; 222 | }else if( joypadState & BUTTON_Z ) { 223 | maxAngle = 40; 224 | } 225 | } 226 | 227 | 228 | 229 | int main(bool hard) 230 | { 231 | VDP_setScreenWidth320(); 232 | // set colors 233 | PAL_setPalette( PAL0, bg_pal.data, CPU) ; 234 | PAL_setPalette( PAL1, gun_bike_pal.data, CPU ); 235 | PAL_setPalette( PAL2, gun_bike_rider_pal.data, CPU ); 236 | PAL_setPalette( PAL3, boss_truck_pal.data, CPU ); 237 | 238 | //setup scroll values 239 | setupB(); 240 | 241 | // set scrolling mode to support rotation 242 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_COLUMN); 243 | 244 | // get tile positions in VRAM. 245 | int ind = TILE_USER_INDEX; 246 | int indexA = ind; 247 | // Load the plane tiles into VRAM 248 | VDP_loadTileSet(bg_boss_truck.tileset, ind, DMA); 249 | int indexB = ind + bg_boss_truck.tileset->numTile; // new 250 | VDP_loadTileSet(bg.tileset, indexB, DMA); 251 | 252 | // put out the image 253 | VDP_setTileMapEx(BG_A, bg_boss_truck.tilemap, TILE_ATTR_FULL(PAL3, TRUE, FALSE, FALSE, indexA), 254 | 0, // Plane X destination 255 | 0, // plane Y destination 256 | 0, // Region X start position 257 | 0, // Region Y start position 258 | PLANE_MAX_TILE, // width (went with 64 becasue default width is 64. Viewable screen is 40) 259 | 28, // height 260 | CPU); 261 | 262 | VDP_setTileMapEx(BG_B, bg.tilemap, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, indexB), 263 | 0, 264 | 0, 265 | 0, 266 | 0, 267 | PLANE_MAX_TILE, 268 | 28, 269 | CPU); 270 | 271 | 272 | 273 | ////////////////////////////////////////////////////////////// 274 | SPR_init(); 275 | 276 | Sprite * gunbikerider1 = NULL; 277 | Sprite * gunbike1 = NULL; 278 | int gunbikerider1_pos_x = 91; 279 | int gunbikerider1_pos_y = 140; 280 | gunbikerider1 = SPR_addSprite( &gun_bike_rider_big, 281 | gunbikerider1_pos_x, 282 | gunbikerider1_pos_y, 283 | TILE_ATTR( PAL2, 284 | 1, // tile priority 285 | FALSE, // flip sprite vertically 286 | FALSE // flip sprite horizontally 287 | )); 288 | 289 | int gunbike1_pos_x = 55; 290 | int gunbike1_pos_y = 152; 291 | gunbike1 = SPR_addSprite( &gun_bike_big, 292 | gunbike1_pos_x, 293 | gunbike1_pos_y, 294 | TILE_ATTR( PAL1, 295 | 1, 296 | FALSE, 297 | FALSE )); 298 | 299 | 300 | Sprite *truckWheelSprite1 = NULL; 301 | fix32 truckWheelPosX1 = FIX32(55.0); 302 | fix32 truckWheelPosY1 = FIX32(135.0); 303 | truckWheelSprite1 = SPR_addSprite( &boss_truck_wheel, // Sprite defined in resources 304 | fix32ToInt(truckWheelPosX1),// starting X position 305 | fix32ToInt(truckWheelPosY1),// starting Y position 306 | TILE_ATTR( PAL3, // specify palette 307 | 1, // Tile priority ( with background) 308 | FALSE, // flip the sprite vertically? 309 | FALSE // flip the sprite horizontally 310 | )); 311 | 312 | Sprite *truckWheelSprite2 = NULL; 313 | fix32 truckWheelPosX2 = FIX32(186.0); 314 | fix32 truckWheelPosY2 = FIX32(135.0); 315 | truckWheelSprite2 = SPR_addSprite( &boss_truck_wheel, // Sprite defined in resources 316 | fix32ToInt(truckWheelPosX2),// starting X position 317 | fix32ToInt(truckWheelPosY2),// starting Y position 318 | TILE_ATTR( PAL3, // specify palette 319 | TRUE, // Tile priority ( with background) 320 | FALSE, // flip the sprite vertically? 321 | FALSE // flip the sprite horizontally 322 | )); 323 | 324 | 325 | Sprite *treeSprite = NULL; 326 | fix32 treePosX = FIX32(152.0); 327 | fix32 treePosY = FIX32(54.0); 328 | treeSprite = SPR_addSprite( &tree, // Sprite defined in resources 329 | fix32ToInt(treePosX),// starting X position 330 | fix32ToInt(treePosY),// starting Y position 331 | TILE_ATTR( PAL0, // specify palette 332 | FALSE, // Tile priority ( with background) 333 | FALSE, // flip the sprite vertically? 334 | FALSE // flip the sprite horizontally 335 | )); 336 | 337 | 338 | 339 | u16 currAngle = 0; 340 | setAngle(currAngle, 150); 341 | int stepDir = 1; 342 | while (TRUE) 343 | { 344 | // read joypad to set max angle dynamically 345 | readJoypad(JOY_1); 346 | 347 | // sprite 348 | treePosX = fix32Sub(treePosX, FIX32(2.2)); 349 | SPR_setPosition(treeSprite, fix32ToInt(treePosX), 54); 350 | 351 | // Fake rotation 352 | currAngle += stepDir; 353 | if (stepDir == 1 && currAngle < 512) 354 | { 355 | if (currAngle == maxAngle) 356 | { 357 | stepDir = -1; 358 | } 359 | } 360 | else if (stepDir == -1 && currAngle == 0) 361 | { 362 | currAngle = 1024; 363 | } 364 | else if (stepDir == -1 && currAngle > 512) 365 | { 366 | if (currAngle == 1024 - maxAngle) 367 | { 368 | stepDir = 1; 369 | } 370 | } 371 | else if (stepDir == 1 && currAngle == 1024) 372 | { 373 | currAngle = 0; 374 | } 375 | setAngle(currAngle, 130); 376 | 377 | // set scrolling to fake the rotaiton. 378 | VDP_setHorizontalScrollLine(BG_A, START_ROW_A, hScrollA, ROWS_A, DMA); 379 | VDP_setVerticalScrollTile(BG_A, START_COL_A, vScrollA, COLS_A, DMA); 380 | 381 | // parallax scrolling. 382 | updateScroll(BG_B, bg.tilemap, indexB, 383 | speedB, 384 | planeOffsetB, 385 | imageOffsetB, 386 | lastSrcColB, 387 | lastDstColB, 388 | hScrollB, 389 | START_ROW_B, 390 | ROWS_B); 391 | VDP_setHorizontalScrollLine(BG_B, START_ROW_B * 8, hScrollB, ROWS_B * 8, DMA); 392 | 393 | SPR_update(); 394 | 395 | // let SGDK do its thing 396 | SYS_doVBlankProcess(); 397 | } 398 | return 0; 399 | } 400 | -------------------------------------------------------------------------------- /ScrollingWithCamera/res/planes/plane_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/ScrollingWithCamera/res/planes/plane_a.png -------------------------------------------------------------------------------- /ScrollingWithCamera/res/resources.res: -------------------------------------------------------------------------------- 1 | TILESET plane_a_tileset "planes/plane_a.png" NONE ALL 2 | MAP plane_a_map "planes/plane_a.png" plane_a_tileset NONE 0 3 | 4 | SPRITE ship "sprites/ship.png" 3 3 NONE 5 | SPRITE shot "sprites/shot_sheet.png" 1 1 NONE 2 6 | SPRITE rock "sprites/rock.png" 4 4 NONE 8 7 | SPRITE explosion "sprites/explosion.png" 4 4 NONE 0 8 | 9 | PALETTE shot_pal "sprites/shot_sheet.png" 10 | PALETTE plane_pal "planes/plane_a.png" 11 | PALETTE ship_pal "sprites/ship.png" 12 | PALETTE rock_pal "sprites/rock.png" -------------------------------------------------------------------------------- /ScrollingWithCamera/res/sprites/explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/ScrollingWithCamera/res/sprites/explosion.png -------------------------------------------------------------------------------- /ScrollingWithCamera/res/sprites/rock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/ScrollingWithCamera/res/sprites/rock.png -------------------------------------------------------------------------------- /ScrollingWithCamera/res/sprites/ship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/ScrollingWithCamera/res/sprites/ship.png -------------------------------------------------------------------------------- /ScrollingWithCamera/res/sprites/shot_sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/ScrollingWithCamera/res/sprites/shot_sheet.png -------------------------------------------------------------------------------- /ScrollingWithCamera/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | 4 | #define PLAYER_FRAME_COUNT 16 5 | #define MAX_ROTATION_INDEX FIX32(15) 6 | #define MIN_ROTATION_INDEX FIX32(0) 7 | #define PLAYER_WIDTH 24 8 | #define PLAYER_HEIGHT 24 9 | #define PLAYER_SHOT_WIDTH 8 10 | #define PLAYER_SHOT_HEIGHT 8 11 | #define MAX_PLAYER_SHOTS 4 12 | 13 | 14 | #define MAX_ROCKS 18 15 | #define MAX_EXPLOSIONS 6 16 | 17 | #define MAP_WIDTH 1280 18 | #define MAP_HEIGHT 896 19 | 20 | #define SCR_WIDTH 320 21 | #define SCR_HEIGHT 224 22 | 23 | #define CAMERA_PADDING 90 24 | 25 | // Struct for managing sprites 26 | typedef struct { 27 | Sprite *sprite; 28 | fix32 pos_x; 29 | fix32 pos_y; 30 | fix32 vel_x; 31 | fix32 vel_y; 32 | 33 | s32 hitbox_x1; 34 | s32 hitbox_y1; 35 | s32 hitbox_x2; 36 | s32 hitbox_y2; 37 | 38 | bool active; 39 | u16 ticks; 40 | } CP_SPRITE; 41 | 42 | 43 | // map variables 44 | Map *map_a; 45 | s32 camPosX;// Camera position is used to move the MAP 46 | s32 camPosY; 47 | 48 | 49 | // player variables 50 | CP_SPRITE player; 51 | fix32 playerRotation; 52 | bool updatePlayerPosition; 53 | fix32 deltaX[PLAYER_FRAME_COUNT]; 54 | fix32 deltaY[PLAYER_FRAME_COUNT]; 55 | CP_SPRITE playerShots[ MAX_PLAYER_SHOTS ]; 56 | 57 | 58 | // Rocks 59 | CP_SPRITE rocks[MAX_ROCKS]; 60 | 61 | // explosions 62 | CP_SPRITE explosions[MAX_EXPLOSIONS]; 63 | u16 nextExplosion = 0; 64 | 65 | 66 | static void addExplosion( fix32 pos_x, fix32 pos_y ) { 67 | // check if the current explosion is free to use 68 | if( explosions[ nextExplosion ].active == FALSE ){ 69 | // not in use, so place it on screen 70 | explosions[nextExplosion].pos_x = pos_x; 71 | explosions[nextExplosion].pos_y = pos_y; 72 | explosions[nextExplosion].active = TRUE; 73 | explosions[nextExplosion].ticks = 0; 74 | SPR_setVisibility( explosions[nextExplosion].sprite, VISIBLE); 75 | SPR_setPosition( explosions[nextExplosion].sprite,fix32ToInt(explosions[nextExplosion].pos_x),fix32ToInt(explosions[nextExplosion].pos_y)); 76 | 77 | // update current explosion index for next request 78 | ++nextExplosion; 79 | if( nextExplosion >= MAX_EXPLOSIONS ) { 80 | nextExplosion = 0; 81 | } 82 | } 83 | } 84 | 85 | 86 | 87 | static void updateCameraPos() { 88 | // figure out where the player is. 89 | s32 px = fix32ToInt( player.pos_x ); 90 | s32 py = fix32ToInt( player.pos_y ); 91 | 92 | s32 playerScreenX = px - camPosX; 93 | s32 playerScreenY = py - camPosY; 94 | 95 | // Adjust new camera X position based on player position 96 | s32 newCamX; 97 | // check if the player X position is too close to the right edge of the screen 98 | if( playerScreenX > SCR_WIDTH - CAMERA_PADDING - PLAYER_WIDTH ) { 99 | newCamX = px - ( SCR_WIDTH - CAMERA_PADDING - PLAYER_WIDTH ); 100 | } else if( playerScreenX < CAMERA_PADDING ) { // check if the player is too close to the left 101 | newCamX = px - CAMERA_PADDING; 102 | } else { 103 | newCamX = camPosX; // no change to camera position. 104 | } 105 | 106 | // Adjust camera Y position based on player position 107 | s32 newCamY; 108 | // check if the player Y position is too close to the bottom edge of the screen 109 | if( playerScreenY > SCR_HEIGHT - CAMERA_PADDING - PLAYER_HEIGHT ) { 110 | newCamY = py - ( SCR_HEIGHT - CAMERA_PADDING - PLAYER_HEIGHT ) ; 111 | } else if( playerScreenY < CAMERA_PADDING ) { // is player too close to the top of the screen? 112 | newCamY = py - CAMERA_PADDING; 113 | } else { 114 | newCamY = camPosY; // no change to camera position. 115 | } 116 | 117 | 118 | // handle camera position at edges 119 | if ( newCamX < 0 ) { // don't move past the left edge of the scroll image. 120 | newCamX = 0; 121 | } else if ( newCamX > (MAP_WIDTH - SCR_WIDTH )) { // don't move past the right edge 122 | newCamX = MAP_WIDTH - SCR_WIDTH ; 123 | } 124 | if ( newCamY < 0 ) { // don't move past the top of the scroll image 125 | newCamY = 0; 126 | } else if ( newCamY > (MAP_HEIGHT - SCR_HEIGHT )) { // don't move past the bottom 127 | newCamY = MAP_HEIGHT - SCR_HEIGHT ; 128 | } 129 | 130 | // Store the values 131 | camPosX = newCamX; 132 | camPosY = newCamY; 133 | // Update the MAP position 134 | MAP_scrollTo( map_a, camPosX, camPosY ); 135 | } 136 | 137 | static void inputCallback( u16 joy, u16 changed, u16 state ) { 138 | // create a shot if available 139 | if( changed & state & BUTTON_A ) { 140 | for( u16 i = 0; i < MAX_PLAYER_SHOTS; ++i ) { 141 | if( playerShots[i].active == FALSE ) { 142 | // create a new one 143 | 144 | u16 rot = fix32ToInt( playerRotation); 145 | playerShots[i].pos_x = player.pos_x + FIX32((PLAYER_WIDTH-PLAYER_SHOT_WIDTH)/2) + fix32Mul( deltaX[rot], FIX32(2.0)); 146 | playerShots[i].pos_y = player.pos_y + FIX32((PLAYER_HEIGHT-PLAYER_SHOT_WIDTH)/2) + fix32Mul( deltaY[rot], FIX32(2.0)); 147 | playerShots[i].vel_x = fix32Mul( deltaX[rot], FIX32(2.0)); 148 | playerShots[i].vel_y = fix32Mul( deltaY[rot], FIX32(2.0)); 149 | playerShots[i].active = TRUE; 150 | playerShots[i].ticks = 0; 151 | break; 152 | 153 | } 154 | } 155 | } 156 | } 157 | 158 | 159 | static void handleInput() { 160 | u16 value = JOY_readJoypad(JOY_1); 161 | 162 | if( value & BUTTON_LEFT ) { 163 | playerRotation += FIX32( 0.25 ); // using fixed point to slow down rotation speed 164 | if( playerRotation > MAX_ROTATION_INDEX ) { 165 | playerRotation = MIN_ROTATION_INDEX; 166 | } 167 | int rot = fix32ToInt( playerRotation); 168 | SPR_setAnim( player.sprite, rot); 169 | } else if( value & BUTTON_RIGHT ) { 170 | playerRotation -= FIX32( 0.25 ); 171 | if( playerRotation < MIN_ROTATION_INDEX ) { 172 | playerRotation = MAX_ROTATION_INDEX; 173 | } 174 | int rot = fix32ToInt( playerRotation); 175 | SPR_setAnim( player.sprite, rot); 176 | } 177 | 178 | 179 | if( value & BUTTON_UP ) { 180 | // signal update to uplayer position. 181 | updatePlayerPosition = TRUE; 182 | } else { 183 | updatePlayerPosition = FALSE; 184 | } 185 | 186 | } 187 | 188 | static void update() { 189 | // update player 190 | if( updatePlayerPosition == TRUE ) { 191 | int rot = fix32ToInt( playerRotation); 192 | player.pos_x = player.pos_x + deltaX[rot]; 193 | player.pos_y = player.pos_y + deltaY[rot]; 194 | 195 | 196 | if( player.pos_x< FIX32( -6.0 ) ) { 197 | player.pos_x = FIX32( -6.0 ); 198 | } else if( player.pos_x > FIX32( MAP_WIDTH - PLAYER_WIDTH + 6.0 )) { 199 | player.pos_x = FIX32( MAP_WIDTH - PLAYER_WIDTH + 6.0 ); 200 | } 201 | 202 | if( player.pos_y< FIX32( -6.0) ) { 203 | player.pos_y = FIX32( -6.0 ); 204 | } else if( player.pos_y > FIX32( MAP_HEIGHT - PLAYER_HEIGHT + 6.0 )) { 205 | player.pos_y = FIX32( MAP_HEIGHT - PLAYER_HEIGHT + 6.0 ); 206 | } 207 | 208 | 209 | } 210 | 211 | 212 | // move shots 213 | for( u16 i=0; i < MAX_PLAYER_SHOTS; ++i ) { 214 | if( playerShots[i].active == TRUE ) { 215 | playerShots[i].pos_x += playerShots[i].vel_x; 216 | playerShots[i].pos_y += playerShots[i].vel_y; 217 | s32 x = fix32ToInt(playerShots[i].pos_x) - camPosX; 218 | s32 y = fix32ToInt(playerShots[i].pos_y) - camPosY; 219 | if( x >= 0 && x < SCR_WIDTH && y >= 0 && y < SCR_HEIGHT ) { 220 | SPR_setVisibility( playerShots[i].sprite, VISIBLE); 221 | SPR_setPosition(playerShots[i].sprite, fix32ToInt(playerShots[i].pos_x) - camPosX, fix32ToInt(playerShots[i].pos_y) - camPosY ); 222 | } else { 223 | // shot reached the screen edge, deactivate and hide it. 224 | playerShots[i].active = FALSE; 225 | SPR_setVisibility( playerShots[i].sprite, HIDDEN); 226 | } 227 | } else { 228 | // hide inactive shots 229 | SPR_setVisibility( playerShots[i].sprite, HIDDEN); 230 | } 231 | } 232 | 233 | 234 | 235 | 236 | // rocks 237 | for( u16 i=0; i < MAX_ROCKS; ++i ) { 238 | if( rocks[i].active == TRUE ) { 239 | rocks[i].pos_x += rocks[i].vel_x; 240 | if( rocks[i].pos_x < FIX32(-32) ) { rocks[i].pos_x = FIX32(MAP_WIDTH);} 241 | else if( rocks[i].pos_x >FIX32(MAP_WIDTH) ) { rocks[i].pos_x = FIX32(-32);} 242 | 243 | rocks[i].pos_y += rocks[i].vel_y; 244 | if( rocks[i].pos_y < FIX32(-32) ) { rocks[i].pos_y = FIX32(MAP_HEIGHT);} 245 | else if( rocks[i].pos_y >FIX32(MAP_HEIGHT) ) { rocks[i].pos_y = FIX32(-32);} 246 | 247 | // only show rock if it's visible. 248 | s32 x = fix32ToInt(rocks[i].pos_x) - camPosX; 249 | s32 y = fix32ToInt(rocks[i].pos_y) - camPosY; 250 | if( x >= -32 && x < SCR_WIDTH && y >= -32 && y < SCR_HEIGHT ) { 251 | SPR_setVisibility( rocks[i].sprite, VISIBLE); 252 | SPR_setPosition(rocks[i].sprite, fix32ToInt(rocks[i].pos_x) - camPosX, fix32ToInt(rocks[i].pos_y) - camPosY ); 253 | } else { 254 | // shot reached the screen edge, hide it. 255 | SPR_setVisibility( rocks[i].sprite, HIDDEN); 256 | } 257 | } else { 258 | // hide inactive rocks 259 | SPR_setVisibility( rocks[i].sprite, HIDDEN); 260 | } 261 | } 262 | 263 | for( u16 i=0; i < MAX_EXPLOSIONS; ++i ) { 264 | if( explosions[i].active == TRUE ) { 265 | explosions[i].ticks += 1; 266 | if( explosions[i].ticks < 9 ) { 267 | // SPR_setFrame( explosions[i].sprite, explosions[i].ticks ); 268 | SPR_setPosition(explosions[i].sprite, fix32ToInt(explosions[i].pos_x) - camPosX, fix32ToInt(explosions[i].pos_y) - camPosY ); 269 | SPR_setAnimAndFrame( explosions[i].sprite, i%4, explosions[i].ticks ); 270 | } 271 | else { 272 | explosions[i].active = FALSE; 273 | SPR_setVisibility( explosions[i].sprite, HIDDEN); 274 | } 275 | } 276 | 277 | } 278 | // set the player position. 279 | SPR_setPosition( player.sprite, fix32ToInt( player.pos_x) - camPosX, fix32ToInt( player.pos_y ) - camPosY ); 280 | 281 | // change position of the MAP 282 | updateCameraPos(); 283 | } 284 | 285 | static void checkCollisions() { 286 | // simple collision just checks bad sprites against good. 287 | for( u16 i=0; i < MAX_ROCKS; ++i ) { 288 | if( rocks[i].active == TRUE ) { 289 | // check if ship hits a rock 290 | if( (rocks[i].pos_x + rocks[i].hitbox_x1) < (player.pos_x + player.hitbox_x2) && 291 | (rocks[i].pos_x + rocks[i].hitbox_x2) > (player.pos_x + player.hitbox_x1) && 292 | (rocks[i].pos_y + rocks[i].hitbox_y1) < (player.pos_y + player.hitbox_y2) && 293 | (rocks[i].pos_y + rocks[i].hitbox_y2) > (player.pos_y + player.hitbox_y1) ) 294 | { 295 | rocks[i].active = FALSE; 296 | SPR_setVisibility( rocks[i].sprite, HIDDEN); 297 | addExplosion( rocks[i].pos_x, rocks[i].pos_y ); 298 | } 299 | 300 | // check if a player shot hits a rock. 301 | for( u16 j=0; j < MAX_PLAYER_SHOTS; ++j ) { 302 | if( 303 | playerShots[j].active == TRUE && 304 | (rocks[i].pos_x + rocks[i].hitbox_x1) < (playerShots[j].pos_x + FIX32(4)) && 305 | (rocks[i].pos_x + rocks[i].hitbox_x2) > (playerShots[j].pos_x + FIX32(4)) && 306 | (rocks[i].pos_y + rocks[i].hitbox_y1) < (playerShots[j].pos_y + FIX32(4)) && 307 | (rocks[i].pos_y + rocks[i].hitbox_y2) > (playerShots[j].pos_y + FIX32(4)) ) 308 | { 309 | rocks[i].active = FALSE; 310 | SPR_setVisibility( rocks[i].sprite, HIDDEN); 311 | playerShots[j].active = FALSE; 312 | SPR_setVisibility( playerShots[j].sprite, HIDDEN); 313 | addExplosion( rocks[i].pos_x, rocks[i].pos_y ); 314 | break; // stop checking once it's been hit 315 | } 316 | } 317 | } 318 | } 319 | } 320 | 321 | 322 | 323 | 324 | 325 | static void createPlayerShots() { 326 | fix32 xpos = FIX32(-16); 327 | fix32 ypos = FIX32(-16); 328 | 329 | for( u16 i=0; i < MAX_PLAYER_SHOTS; ++i ) { 330 | playerShots[i].pos_x = xpos; 331 | playerShots[i].pos_y = ypos; 332 | playerShots[i].vel_x = FIX32(0.0); 333 | playerShots[i].vel_y = FIX32(0.0); 334 | playerShots[i].active = FALSE; 335 | playerShots[i].hitbox_x1 = FIX32(3); 336 | playerShots[i].hitbox_y1 = FIX32(3); 337 | playerShots[i].hitbox_x2 = FIX32(4); 338 | playerShots[i].hitbox_y2 = FIX32(4); 339 | 340 | playerShots[i].sprite = SPR_addSprite( &shot, xpos, ypos, TILE_ATTR( PAL0, 0, FALSE, FALSE )); 341 | SPR_setAnim( playerShots[i].sprite, 2 ); 342 | } 343 | 344 | } 345 | 346 | static void createRocks() { 347 | fix32 ypos = FIX32(0); 348 | fix32 xpos = FIX32(0); 349 | for( u16 i=0; i < MAX_ROCKS; ++i ) { 350 | rocks[i].pos_x = FIX32(random()%(MAP_WIDTH-32) + i ); // random starting position for rock sprites 351 | rocks[i].pos_y = FIX32(random()%(MAP_HEIGHT-32)+ i); 352 | 353 | // use ranodm direction for rock motion 354 | u16 rot = random() % 16; 355 | fix32 vel = FIX32(0.8); 356 | rocks[i].vel_x = fix32Mul( vel, deltaX[rot] ); 357 | rocks[i].vel_y = fix32Mul( vel, deltaY[rot] ); 358 | rocks[i].active = TRUE; 359 | rocks[i].hitbox_x1 = FIX32(2); 360 | rocks[i].hitbox_y1 = FIX32(2); 361 | rocks[i].hitbox_x2 = FIX32(30); 362 | rocks[i].hitbox_y2 = FIX32(30); 363 | 364 | //rocks[i].sprite = SPR_addSprite( &rock, -32, -32, TILE_ATTR( PAL3, 0, FALSE, FALSE )); 365 | rocks[i].sprite = SPR_addSprite( &rock, rocks[i].pos_x, rocks[i].pos_y, TILE_ATTR( PAL3, 0, FALSE, FALSE )); 366 | SPR_setAnim( rocks[i].sprite, 0 ); 367 | } 368 | } 369 | 370 | static void createExplosions() { 371 | fix32 xpos = FIX32(0); 372 | fix32 ypos = FIX32(264); 373 | 374 | for( u16 i=0; i < MAX_EXPLOSIONS; ++i ) { 375 | explosions[i].pos_x = xpos; 376 | explosions[i].pos_y = ypos; 377 | explosions[i].vel_x = FIX32(0); 378 | explosions[i].vel_y = FIX32(0); 379 | explosions[i].active = FALSE; 380 | explosions[i].hitbox_x1 = FIX32(0); 381 | explosions[i].hitbox_y1 = FIX32(0); 382 | explosions[i].hitbox_x2 = FIX32(0); 383 | explosions[i].hitbox_y2 = FIX32(0); 384 | 385 | explosions[i].sprite = SPR_addSprite( &explosion, fix32ToInt(xpos), fix32ToInt(ypos), TILE_ATTR( PAL0, 0, FALSE, FALSE )); 386 | SPR_setAnim( explosions[i].sprite, i % 4 ); 387 | 388 | SPR_setVisibility( explosions[i].sprite, HIDDEN ); 389 | SPR_setDepth( explosions[i].sprite, SPR_MIN_DEPTH ); 390 | } 391 | 392 | } 393 | 394 | 395 | int main( bool hard ) { 396 | 397 | // clear 398 | memset( playerShots, 0, sizeof(playerShots) ); 399 | memset( rocks, 0, sizeof(rocks) ); 400 | memset( explosions, 0, sizeof(explosions) ); 401 | 402 | 403 | PAL_setPalette( PAL0, shot_pal.data, CPU); 404 | PAL_setPalette( PAL1, plane_pal.data, CPU); 405 | PAL_setPalette( PAL2, ship_pal.data, CPU); 406 | PAL_setPalette( PAL3, rock_pal.data, CPU); 407 | 408 | // Load the plane tiles into VRAM 409 | int ind = TILE_USER_INDEX; 410 | VDP_loadTileSet( &plane_a_tileset, ind, DMA ); 411 | 412 | // init plane 413 | map_a = MAP_create( &plane_a_map, BG_A, TILE_ATTR_FULL( PAL1, FALSE, FALSE, FALSE, ind ) ); 414 | camPosX = MAP_WIDTH/2 - SCR_WIDTH/2; 415 | camPosY = MAP_HEIGHT/2 - SCR_HEIGHT/2; 416 | 417 | MAP_scrollTo( map_a, camPosX, camPosY ); 418 | 419 | // Init sprite engine with defaults 420 | SPR_init(); 421 | 422 | // Setup the player sprite 423 | player.pos_x = FIX32( MAP_WIDTH/2 - 12 ); 424 | player.pos_y = FIX32( MAP_HEIGHT/2 - 12 ); 425 | player.sprite = SPR_addSprite( &ship, fix32ToInt(player.pos_x) - camPosX, fix32ToInt(player.pos_y) - camPosY, TILE_ATTR(PAL2, 0, FALSE, FALSE )); 426 | player.hitbox_x1 = FIX32(5); 427 | player.hitbox_y1 = FIX32(5); 428 | player.hitbox_x2 = FIX32(19); 429 | player.hitbox_y2 = FIX32(19); 430 | playerRotation = MIN_ROTATION_INDEX; 431 | SPR_setAnim( player.sprite, fix32ToInt(playerRotation ) ); 432 | 433 | 434 | // setup player motion offset. 435 | deltaX[0] = FIX32( 0.000000 ); 436 | deltaY[0] = FIX32( -2.000000 ); 437 | deltaX[1] = FIX32( -0.765367 ); 438 | deltaY[1] = FIX32( -1.847759 ); 439 | deltaX[2] = FIX32( -1.414214 ); 440 | deltaY[2] = FIX32( -1.414214 ); 441 | deltaX[3] = FIX32( -1.847759 ); 442 | deltaY[3] = FIX32( -0.765367 ); 443 | deltaX[4] = FIX32( -2.000000 ); 444 | deltaY[4] = FIX32( -0.000000 ); 445 | deltaX[5] = FIX32( -1.847759 ); 446 | deltaY[5] = FIX32( 0.765367 ); 447 | deltaX[6] = FIX32( -1.414214 ); 448 | deltaY[6] = FIX32( 1.414214 ); 449 | deltaX[7] = FIX32( -0.765367 ); 450 | deltaY[7] = FIX32( 1.847759 ); 451 | deltaX[8] = FIX32( -0.000000 ); 452 | deltaY[8] = FIX32( 2.000000 ); 453 | deltaX[9] = FIX32( 0.765367 ); 454 | deltaY[9] = FIX32( 1.847759 ); 455 | deltaX[10] = FIX32( 1.414214 ); 456 | deltaY[10] = FIX32( 1.414214 ); 457 | deltaX[11] = FIX32( 1.847759 ); 458 | deltaY[11] = FIX32( 0.765367 ); 459 | deltaX[12] = FIX32( 2.000000 ); 460 | deltaY[12] = FIX32( 0.000000 ); 461 | deltaX[13] = FIX32( 1.847759 ); 462 | deltaY[13] = FIX32( -0.765367 ); 463 | deltaX[14] = FIX32( 1.414214 ); 464 | deltaY[14] = FIX32( -1.414214 ); 465 | deltaX[15] = FIX32( 0.765367 ); 466 | deltaY[15] = FIX32( -1.847759 ); 467 | 468 | createExplosions(); 469 | createPlayerShots(); 470 | createRocks(); 471 | 472 | JOY_setEventHandler( &inputCallback ); 473 | 474 | while(TRUE) { 475 | 476 | handleInput(); 477 | 478 | update(); 479 | 480 | checkCollisions(); 481 | 482 | SPR_update(); 483 | 484 | SYS_doVBlankProcess(); 485 | } 486 | return 0; 487 | } 488 | 489 | -------------------------------------------------------------------------------- /VScrollTiled/res/bg/planeb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/VScrollTiled/res/bg/planeb.png -------------------------------------------------------------------------------- /VScrollTiled/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE plane_b "bg/planeb.png" 0 2 | 3 | PALETTE plane_b_pal "bg/planeb.png" 4 | -------------------------------------------------------------------------------- /VScrollTiled/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | 4 | 5 | ///////////////////////////////////////////////////////////////////// 6 | // Vertical Tiled Scrolling 7 | // 8 | // default SGDK width is 512 pixels (64 tiles) 9 | #define PLANE_MAX_PIXEL 384 10 | #define PLANE_MAX_HORIZONTAL_TILE 48 11 | #define PLANE_MAX_VERTICAL_TILE 32 12 | 13 | s16 vScrollB[20] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 14 | s16 planeBDeltas[20] = {9, 9, 7, 7, 5, 5, 5, 4, 3, 2, 2, 3, 4, 5, 5, 5, 7, 7, 9, 9}; 15 | 16 | 17 | 18 | int main(bool hard) 19 | { 20 | 21 | // SETUP backgroupd 22 | VDP_setBackgroundColor(16); 23 | VDP_setScreenWidth320(); 24 | 25 | // Set Colors 26 | PAL_setPalette(PAL0, plane_b_pal.data, CPU); 27 | PAL_setColor(0, 0x0000); 28 | 29 | // set scrolling modes to support fake rotation 30 | VDP_setScrollingMode(HSCROLL_PLANE, VSCROLL_COLUMN); 31 | 32 | // get initial tile position in VRAM 33 | int ind = TILE_USER_INDEX; 34 | VDP_loadTileSet(plane_b.tileset, ind, DMA); 35 | 36 | // SImple image for BG_B. We're not changing it during the level 37 | VDP_drawImageEx(BG_B, &plane_b, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 38 | 39 | 40 | while (TRUE) 41 | { 42 | // scroll the asteroids in BG_B 43 | for (int i = 0; i < 20; i++) 44 | { 45 | vScrollB[i] -= planeBDeltas[i]; 46 | } 47 | 48 | VDP_setVerticalScrollTile(BG_B, 0, vScrollB, 20, DMA); // use array to set plane offsets 49 | SYS_doVBlankProcess(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /waves/ex1/README.md: -------------------------------------------------------------------------------- 1 | # Simple Wave Example 2 | 3 | -------------------------------------------------------------------------------- /waves/ex1/res/bg/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/waves/ex1/res/bg/bg.png -------------------------------------------------------------------------------- /waves/ex1/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE bg "bg/bg.png" BEST 2 | PALETTE bg_pal "bg/bg.png" 3 | 4 | 5 | -------------------------------------------------------------------------------- /waves/ex1/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | 4 | #define TOTAL_LINES 224 5 | 6 | 7 | int main(bool hard) { 8 | 9 | ////////////////////////////////////////////////////////////// 10 | // setup screen and palettes 11 | VDP_setBackgroundColor(16); 12 | VDP_setScreenWidth320(); 13 | 14 | PAL_setPalette( PAL0, bg_pal.data, CPU ); 15 | 16 | ////////////////////////////////////////////////////////////// 17 | // setup scrolling 18 | int ind = TILE_USER_INDEX; 19 | VDP_drawImageEx(BG_B, &bg, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 20 | ind += bg.tileset->numTile; 21 | 22 | // use LINE scroll for horizontal 23 | VDP_setScrollingMode( HSCROLL_LINE, VSCROLL_PLANE); 24 | 25 | s16 hScroll[TOTAL_LINES]; 26 | memset( hScroll, 0, sizeof(hScroll)); 27 | 28 | u16 sinPerLine = 5; // Elements to jump in sin() per line. Larger values give us faster waves. 29 | fix16 amplitude = FIX16( 10.0 ); // Amplitude sets how big the waves are. 30 | s16 offset = -40; // shift left a bit. 31 | for( u16 i = 0; i < TOTAL_LINES; ++i ) { 32 | hScroll[i] = fix16ToInt( fix16Mul( sinFix16(i * sinPerLine ), amplitude ) ) + offset; 33 | } 34 | 35 | 36 | ////////////////////////////////////////////////////////////// 37 | // main loop. 38 | u16 sinOffset = 0; // Step basically tells us where we're starting in the sin table. 39 | while(TRUE) 40 | { 41 | // read joypad to set sinewave parameters 42 | u16 joypad = JOY_readJoypad( JOY_1 ); 43 | if( joypad & BUTTON_UP ) { 44 | sinPerLine++; 45 | } else if( joypad & BUTTON_DOWN ) { 46 | sinPerLine--; 47 | } 48 | if( joypad & BUTTON_LEFT ) { 49 | amplitude = amplitude - FIX16(1); 50 | } else if( joypad & BUTTON_RIGHT ) { 51 | amplitude = amplitude + FIX16(1); 52 | } 53 | 54 | if( joypad & BUTTON_A) { 55 | sinPerLine = 5; 56 | amplitude = FIX16( 10.0 ); 57 | } 58 | if( joypad & BUTTON_B) { 59 | sinPerLine = 10; 60 | amplitude = FIX16( 30.0 ); 61 | } 62 | if( joypad & BUTTON_C) { 63 | sinPerLine = 80; 64 | amplitude = FIX16( 0.5 ); 65 | } 66 | 67 | // write params to the screen. 68 | char message[40]; 69 | char amps[5]; 70 | fix16ToStr( amplitude, amps, 1 ); 71 | strclr(message); 72 | sprintf( message, "sin per line: %d amplitude: %s ", sinPerLine, amps ); 73 | VDP_drawText(message, 3, 1 ); 74 | 75 | ////////////////////////////////////////////////////////////// 76 | // This is what matters right here: 77 | // calculate the offsets per line using SGDK's sin table 78 | // and adjust with params 79 | sinOffset++; // move up in the sine table 80 | for( u16 i = 0; i < TOTAL_LINES; ++i ) { 81 | // compute horizontal offsets with sine table. 82 | hScroll[i] = fix16ToInt( fix16Mul( sinFix16(( i + sinOffset ) * sinPerLine ), amplitude ) ) + offset; 83 | } 84 | 85 | // apply scrolling offsets 86 | VDP_setHorizontalScrollLine (BG_B, 0, hScroll, 223, DMA_QUEUE); 87 | SYS_doVBlankProcess(); 88 | 89 | } 90 | return 0; 91 | 92 | } 93 | -------------------------------------------------------------------------------- /waves/ex2/res/bg/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/waves/ex2/res/bg/bg.png -------------------------------------------------------------------------------- /waves/ex2/res/bg/fg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/waves/ex2/res/bg/fg.png -------------------------------------------------------------------------------- /waves/ex2/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE bg "bg/bg.png" BEST 2 | IMAGE fg "bg/fg.png" BEST 3 | 4 | PALETTE bg_pal "bg/bg.png" 5 | PALETTE fg_pal "bg/fg.png" 6 | 7 | SPRITE bubble "sprites/bubble.png" 4 4 NONE 8 | -------------------------------------------------------------------------------- /waves/ex2/res/sprites/bubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/SGDK_Scrolling/f3467ef45edefbbd6bd5a372af8d341c448d623b/waves/ex2/res/sprites/bubble.png -------------------------------------------------------------------------------- /waves/ex2/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | 4 | #define TOTAL_LINES 224 5 | 6 | #define BUBBLE_COUNT 20 7 | 8 | typedef struct 9 | { 10 | Sprite *sprite; 11 | s16 pos_x; 12 | s16 pos_y; 13 | s16 vel_y; 14 | u16 sinPerLine; 15 | fix16 amplitude; 16 | 17 | } CP_SPRITE; 18 | 19 | CP_SPRITE bubbles[BUBBLE_COUNT]; 20 | 21 | void createBubbles() { 22 | for( u16 i=0; i < BUBBLE_COUNT; ++i ) { 23 | bubbles[i].pos_x = random() % ( 320 - 32); 24 | bubbles[i].pos_y = random() % ( 224 - 32); 25 | bubbles[i].vel_y = random() % ( 4) + 1; 26 | bubbles[i].sinPerLine = random() % 5 + 5; 27 | bubbles[i].amplitude = intToFix16( random() % 7 + 10 ); 28 | bubbles[i].sprite = SPR_addSprite( &bubble, fix16ToInt( bubbles[i].pos_x), bubbles[i].pos_y, TILE_ATTR( PAL1, 0, FALSE, FALSE)); 29 | } 30 | } 31 | 32 | void updateBubbles() { 33 | for( u16 i=0; i < BUBBLE_COUNT; ++i ) { 34 | bubbles[i].pos_y -= bubbles[i].vel_y; 35 | if( bubbles[i].pos_y < -32 ) { 36 | bubbles[i].pos_y = 224; 37 | } 38 | s16 x = fix16ToInt( fix16Mul( sinFix16( bubbles[i].pos_y * bubbles[i].sinPerLine ), bubbles[i].amplitude ) ) + bubbles[i].pos_x; 39 | SPR_setPosition( bubbles[i].sprite, x, bubbles[i].pos_y); 40 | } 41 | 42 | } 43 | 44 | int main(bool hard) { 45 | 46 | ////////////////////////////////////////////////////////////// 47 | // setup screen and palettes 48 | VDP_setBackgroundColor(16); 49 | VDP_setScreenWidth320(); 50 | 51 | PAL_setPalette( PAL0, bg_pal.data, CPU ); 52 | PAL_setPalette( PAL1, fg_pal.data, CPU ); 53 | 54 | ////////////////////////////////////////////////////////////// 55 | // setup scrolling 56 | int ind = TILE_USER_INDEX; 57 | VDP_drawImageEx(BG_B, &bg, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 58 | ind += bg.tileset->numTile; 59 | VDP_drawImageEx(BG_A, &fg, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 60 | ind += fg.tileset->numTile; 61 | 62 | // use LINE scroll for horizontal 63 | VDP_setScrollingMode( HSCROLL_LINE, VSCROLL_PLANE); 64 | 65 | // background 66 | s16 hScrollB[TOTAL_LINES]; 67 | memset( hScrollB, 0, sizeof(hScrollB)); 68 | 69 | 70 | u16 sinPerLine = 5; // Elements to jump in sin() per line. Larger values give us faster waves. 71 | fix16 amplitude = FIX16( 10.0 ); // Amplitude sets how big the waves are. 72 | s16 offsetB = -10; // shift left a bit. 73 | fix16 fOffsetB = FIX16(-10); // shift left a bit. 74 | 75 | 76 | // foreground 77 | s16 hScrollA[TOTAL_LINES]; 78 | s16 offsetA = 0; // shift left a bit. 79 | s16 aDir = -1; 80 | memset( hScrollA, 0, sizeof(hScrollA)); 81 | 82 | 83 | ////////////////////////////////////////////////////////////// 84 | // sprites 85 | SPR_init(); 86 | createBubbles(); 87 | 88 | ////////////////////////////////////////////////////////////// 89 | // main loop. 90 | u16 sinOffset = 0; // Step basically tells us where we're starting in the sin table. 91 | while(TRUE) 92 | { 93 | // read joypad to set horizontal scrolling positions. 94 | u16 joypad = JOY_readJoypad( JOY_1 ); 95 | if( joypad & BUTTON_LEFT ) { 96 | offsetA +=2; 97 | if ( offsetA > 0 ) { 98 | offsetA = 0; 99 | } else { 100 | fOffsetB = fix16Add(fOffsetB, FIX16(0.75)); 101 | offsetB = fix16ToInt( fOffsetB ); 102 | } 103 | } else if( joypad & BUTTON_RIGHT ) { 104 | offsetA -=2; 105 | if ( offsetA < -158 ) { 106 | offsetA = -158; 107 | } else { 108 | fOffsetB = fix16Sub(fOffsetB, FIX16(0.75)); 109 | offsetB = fix16ToInt( fOffsetB ); 110 | } 111 | } 112 | 113 | 114 | ////////////////////////////////////////////////////////////// 115 | // Wavy scrolling 116 | // calculate the offsets per line using SGDK's sin table 117 | // and adjust with params 118 | sinOffset++; // move up in the sine table 119 | for( u16 i = 0; i < TOTAL_LINES; ++i ) { 120 | // compute horizontal offsets with sine table. 121 | hScrollB[i] = fix16ToInt( fix16Mul( sinFix16(( i + sinOffset ) * sinPerLine ), amplitude ) ) + offsetB; 122 | hScrollA[i] = offsetA; 123 | } 124 | 125 | 126 | ////////////////////////////////////////////////////////////// 127 | // update sprites 128 | updateBubbles(); 129 | SPR_update(); 130 | 131 | // apply scrolling offsets 132 | VDP_setHorizontalScrollLine (BG_B, 0, hScrollB, 223, DMA_QUEUE); 133 | VDP_setHorizontalScrollLine (BG_A, 0, hScrollA, 223, DMA_QUEUE); 134 | SYS_doVBlankProcess(); 135 | 136 | } 137 | return 0; 138 | 139 | } 140 | --------------------------------------------------------------------------------