|
Post by 0x8bitdev on Sept 5, 2022 14:45:41 GMT
hyperfighting , I use the same code and it works well with a map scrolling: disp_off(); vsync();
// status bar init scroll( 0, 0, 224, 0, 31, 0x80 );
mpd_init( sbar_map, 0 ); mpd_draw_screen_by_ind_offs( 0, 1792, FALSE );
// scrollable map init mpd_init( map_ind, 2 ); mpd_draw_screen();
disp_on(); vsync();
...
// update loop ...
mpd_move_left() / mpd_move_right()
mpd_update_screen();
scroll( 1, mpd_scroll_x(), 32, 32, 223, 0xC0 );
vsync();
Have you updated the mpd.h ?
|
|
|
Post by hyperfighting on Sept 5, 2022 18:02:48 GMT
0x8bitdev - Thanks for verifying the code. The Map and Status Bar scroll smoothly with no sprite rendering. As soon as I introduce my "sprite" logic glitches occur in the map during and without scrolling. I can only believe that my sprite logic is too slow and creates a glitch for MAPeD. This is a sample of the "How To Play" logic loop. Curious if you slow down you code would it cause MAPeD rendering to glitch (Effect like the Map misaligns itself etc) If I scroll with no status bar (standard setup) there isn't a glitch with the Map Scrolling combined with my "sprite" logic. [upd] Fixed and working! MADeD logic must be before the 'sprite' logic!for(;;) { mpd_clear_update_flags(); //-START- Moved Above "AnimateActor()" now glitch free scrolling with the status bar!!!
PlayerController();//<- Controls scroll speed and calls mpd_move_left() / mpd_move_right() mpd_update_screen(); scroll(1, mpd_scroll_x(), 32, 32, 223, 0xC0);
//-END- Moved Above "AnimateActor()" now glitch free scrolling with the status bar!!!
vramSlot=0; AnimateActor();//<- Once the mpd_update logic was above the sprite logic the scrolling became smooth!!!! spd_SATB_to_VRAM(); vsync(); trig.frameSkip=OFF;//Always reset frameSkip (if it is set to ON) vramSlot=0; UpdateFrame(); }
|
|
|
Post by hyperfighting on Sept 5, 2022 19:11:23 GMT
0x8bitdev - On the next topic. Finally breaking ground on Gameplay! I have been playing with Entities! I can spawn in my main characters no problem thanks to your excellent "game_prototype" example. I have been spending alot of time with this one! On the subject of Enemies instead of loading all enemies at once when the stage loads I want to perform a check per every new screen index. The reason for this is to load enemies into available "slots" and if the enemy is not close by, the sprite render routine is not performed because the enemy is not loaded. I can also reuse "slots" if an enemy is off screen etc. I have been attempting this "put_number( __curr_scr.scr.scr_ind, 5,6,11);" to see I can get the screens current index to print to the screen (no luck of course). I have been reviewing MPD.h but nothing is jumping out at me in terms of a function that returns the current screen index. My base logic will be: IF (NEW_SCREEN_INDEX) { mpd_find_entity_by_base_id( &__scr_data, ENEMY_ID );}
|
|
|
Post by 0x8bitdev on Sept 6, 2022 14:57:30 GMT
I have been playing with Entities! I can spawn in my main characters no problem thanks to your excellent "game_prototype" example. I have been spending alot of time with this one! The 'game_prototype' is WIP. But even now it has a lot of interesting things, you can take for yourself. On the subject of Enemies instead of loading all enemies at once when the stage loads I want to perform a check per every new screen index. The reason for this is to load enemies into available "slots" and if the enemy is not close by, the sprite render routine is not performed because the enemy is not loaded. I can also reuse "slots" if an enemy is off screen etc. Caching entities speeds up access to their data. But as you wish... The only rule to have a stable 60 fps - your game logic must fit into one frame! But somehow I have no doubt that you will rewrite your logic. I have been attempting this "put_number( __curr_scr.scr.scr_ind, 5,6,11);" to see I can get the screens current index to print to the screen (no luck of course). I have been reviewing MPD.h but nothing is jumping out at me in terms of a function that returns the current screen index. My base logic will be: IF (NEW_SCREEN_INDEX) { mpd_find_entity_by_base_id( &__scr_data, ENEMY_ID );} The ' mpd_find_entity_by_base_id' returns a first entity with ENEMY_ID. It is useful when you need to find an unique entity. For example, a player entity. Otherwise, you have to iterate through an entity list and look for entities you need. To get the current screen index you need to know a map scroll values and size of the map width/height in screens. I'll answer a bit later how to make that better.
|
|
|
Post by 0x8bitdev on Sept 6, 2022 14:58:43 GMT
Guys, I have a dilemma. I would not ask this question if I wrote a library for PC. Because in a normal situation, the question of data protection is not a subject for discussion. User access to data is carried out through interface functions. And giving access to data directly to minimize the number of interface functions is just a bad design.
But we use the limited software (HuC) for the limited hardware. And the number of procedures is limited too.
What do you think about direct access to a library data in order to minimize the number of procedures? Is it a time for deprecated data access functions and opened data?
I am against this approach. Because it can break a library behaviour if someone decides to write something directly to the library data.
|
|
|
Post by dshadoff on Sept 6, 2022 15:20:37 GMT
In the days of assembly language on single-threaded systems, one would expose the variables for direct access/set-up prior to the call to the function - full visibility. The caller could, if they knew the program flow, rely on the fact that prior values could remain there in order to save execution cycles. But not all libraries published details about internal variables (although inputs and outputs were required).
In the early days of 'C', this became a tendency among some programmers to use globals, especially when stack accesses were slow. However, as CPUs became faster at stack accesses (hint: much later than this system), and as multithreading operating systems came into use, thread-safeness and re-entrancy of code became cheaper and more necessary, so that stack accesses were no longer penalized, and became normal even for performance-sensitive applications.
Then, in C++, the idea of public/private/etc. came into vogue, and there were accessor functions which effectively did nothing except update those protected values (to this day, I've still never seen such 'setter' functions that actually did range-checking for example). This really doesn't do much to protect anything, but it certainly uses machine cycles.
There is only a small amount of RAM on this system in the first place, and not a lot of CPU cycles to waste on accessor functions. If your goal is to have speed, document the values and allow them to be accessed directly. If your goal is to apply 21st century paradigms on a 1990 machine, it will struggle... but it's your code. You might be able to protect somebody from themselves... but you might not either.
|
|
|
Post by 0x8bitdev on Sept 6, 2022 15:54:15 GMT
dshadoff, Thanks for your opinion. You are absolutely right! On the one hand, when one experienced programmer writes something for another experienced programmer... Nothing bad when they use opened data directly. On the other hand, I'm afraid that the library will be used by people with different backgrounds. And here I can only say that it's not my problem. The only thing I hope I will not fix their bugs!
|
|
|
Post by elmer on Sept 6, 2022 17:08:23 GMT
If your goal is to apply 21st century paradigms on a 1990 machine, it will struggle... but it's your code. You might be able to protect somebody from themselves... but you might not either. I agree ... it's your code and design to do with as you wish, but folks *will* find a way to do something silly and break it whatever you do. The hardware offers no memory-protection, and there are so many inventive ways for a programmer to crash the console! There is only a small amount of RAM on this system in the first place, and not a lot of CPU cycles to waste on accessor functions. If your goal is to have speed, document the values and allow them to be accessed directly. And this is the core advice that I'd give any library author on 8-bit systems. The hardware is just too primitive for modern coding conventions. The closer that the library functionality and interface gets to what an experienced assembly-langauge programmer might write, then the better job the primitive compilers can do. Global variables, and open-access to them are the only way that you're going to get decent performance, and then provide as much high-level functionality as you can within your library itself. The example that you wrote at the top of page 24 is a perfect example of this ... 6 library functions that encapsulate of *lot* of capability for your users ... and then mpd_scroll_x() which should (INHO) just be a global variable for speed. On the other hand, I'm afraid that the library will be used by people with different backgrounds. And here I can only say that it's not my problem. The only thing I hope I will not fix their bugs! Yes, it will be used by different folks ... but it really isn't your problem. There is only so much that you can do to support people, at some point they have to take responsibility for their own usage (and bugs).
|
|
|
Post by 0x8bitdev on Sept 6, 2022 18:54:32 GMT
elmer, Thanks for your opinion. So, it's time for open variables... and deprecated functions!
|
|
|
Post by hyperfighting on Sept 7, 2022 0:47:17 GMT
0x8bitdev - Thanks for the clarification on Base entities. Primary use of the base entity is for the main player. This is how I'm visualizing it now. On second thought based on your feedback regarding spawning entities. Would the fastest way to load MAPeD entities be on initialization of a map? Map Init () - 1. Iterate all enemy entities with mpd_get_entity( ... ) 2. Store the entity value and x,y cords. Gameplay () - 1. Assuming map is moving to the right. 2. Load enemies into available "slots" when enemy X cord is within range EX: 64 pixels in x outside the viewable area. [upd] Just thinking out loud... On an alternate use..."screen index" would be very useful for triggering events when certain screens are in view. I guess alternately using the Map X/Y cords could be used to trigger the same behavior. [upd] I'm guessing to find entities you need to do it on a per screen basis? EX: Cache Entity = ScanScreen1 (cache valid entity property and X,Y), ScanScreen2 (cache valid entity property and X,Y) etc etc until all screens have been scanned and all entity locations have been cached? I have enemy entites located on screen 3 of my layout but I haven't been able to read their coordinates yet. However placing the enemy on the start screen I have no issue getting the coordinates. Potential Feature Request - This stems from 2 observations... 1. You have to zoom out to see long layout's in MAPeD GUI 2. Sometimes I have a colour in MAPeD GUI that I want to copy to SPReD GUI and vice versa...the ability to paste colours between apps would be great / On the other hand a larger colour picker would be awesome too! EX: Assuming colour matching from MAPeD to SPReD - 1. I have to carefully observe the colour location in the colour picker in the MAPeD GUI and carefully select it in SPReD GUI's colour picker. The colour picker is tiny ATM so its tricky to select the correct color you are trying to match. Considering 1 and 2 a Full Screen option would be pretty cool if the colour picker and viewable layout area were to be enlarged.
|
|
|
Post by 0x8bitdev on Sept 8, 2022 10:07:24 GMT
On second thought based on your feedback regarding spawning entities. Would the fastest way to load MAPeD entities be on initialization of a map? Map Init () - 1. Iterate all enemy entities with mpd_get_entity( ... ) 2. Store the entity value and x,y cords. Gameplay () - 1. Assuming map is moving to the right. 2. Load enemies into available "slots" when enemy X cord is within range EX: 64 pixels in x outside the viewable area. Y-e-s! But this is just a special case. The main thing you need to understand is that your game loop must be lightweighted as much as possible. So, everything that can be cached, pre-cached, pre-calculated... etc. should be cached, pre-cached, pre-calculated using LUT(s) etc. on a game level initialization stage. Try to avoid any hard work that loads CPU during a gameplay. Your main goal is to fit your game logic time into one frame, while VDC draws a screen image. Otherwise you'll get unstable fps. That's why, for example, your animation system must be as simple as possible. Here the versatility you were counting on is playing against you. Try to minimize cached data. You may have a lot game screens with a lot of entities. Use every free BIT not byte(!) to describe an object states and to save memory for cached objects. Think you don't have bytes of memory, but you have a lot of bits in a memory! Think in bits, not in bytes. Calculate everything! The max number of screens in your game levels, the max number of entities in one screen. How much memory all the cached data takes up. Split your entities into groups, calculate how much memory each group of entities takes up. You may find that one group of entities takes up much less memory than another one. Make a separate cache for such entities. If something in your game loop can be calculated once in a few frames, don't calculate it every frame. In short, don't relax! [upd] Just thinking out loud... On an alternate use..."screen index" would be very useful for triggering events when certain screens are in view. I guess alternately using the Map X/Y cords could be used to trigger the same behavior. It's completely depends on your gameplay logic and your design document. There are several ways to spawn enemies: 1. Place them on game screens in editor and activate when visible. 2. Using passed level distance and probability of activating enemy or without probability. 3. Using passed game time and probability of activating enemy or without probability. 4. ... BTW, I've added a function for getting a current screen for multidirectional maps - ' mpd_get_curr_screen_ind()' see below. [upd] I'm guessing to find entities you need to do it on a per screen basis? EX: Cache Entity = ScanScreen1 (cache valid entity property and X,Y), ScanScreen2 (cache valid entity property and X,Y) etc etc until all screens have been scanned and all entity locations have been cached? I have enemy entites located on screen 3 of my layout but I haven't been able to read their coordinates yet. However placing the enemy on the start screen I have no issue getting the coordinates. Iterate through all map screens and collect all entities you need: for( scr_n = 0; scr_n < ( mpd_map_scr_height * mpd_map_scr_width ); scr_n++ ) { mpd_get_screen_data( &scr_data, scr_n );
for( ent_n = 0; ent_n < scr_data.scr.ents_cnt; ent_n++ ) { mpd_get_entity( &scr_data, ent_n );
... scr_data.inst_ent <- instanced entity data ... } }
Read the MPD library documentation here or in mpd.h for full details. Potential Feature Request - This stems from 2 observations... 1. You have to zoom out to see long layout's in MAPeD GUI 2. Sometimes I have a colour in MAPeD GUI that I want to copy to SPReD GUI and vice versa...the ability to paste colours between apps would be great / On the other hand a larger colour picker would be awesome too! EX: Assuming colour matching from MAPeD to SPReD - 1. I have to carefully observe the colour location in the colour picker in the MAPeD GUI and carefully select it in SPReD GUI's colour picker. The colour picker is tiny ATM so its tricky to select the correct color you are trying to match. Considering 1 and 2 a Full Screen option would be pretty cool if the colour picker and viewable layout area were to be enlarged. 1. Mouse wheel. 2. Now the MAPeD/SPReD have a minimal palette functionality, just to be able to make something simple. So this is a point for future development. A lot of plans and lack of time. I hope one day I get to it.
|
|
|
Post by 0x8bitdev on Sept 8, 2022 10:17:45 GMT
A small update for MPD library. The same v0.7 - added 'mpd_get_curr_screen_ind()' for multidirectional maps - added open variables to replace deprecated functions - fixed missed tile 4x4 drawing for scrollable maps and also fixed BAT overflow in some cases in 'mpd_draw_screen(...)'Open variables and deprecated functions:~~~~~~~~~~~~~~~~~~~~~~~~~~~#if FLAG_MODE_BIDIR_SCROLLR: mpd_SCR_DATA mpd_curr_scr -> mpd_curr_screen() - DEPRECATED#endif //FLAG_MODE_BIDIR_SCROLL#if FLAG_MODE_MULTIDIR_SCROLLMap width/height in screens:
R: u8 mpd_map_scr_width R: u8 mpd_map_scr_height Active map width/height in pixels = map width/height in pixels - screen width/height in pixels
R: u16 mpd_map_active_width -> mpd_active_map_width() - DEPRECATEDR: u16 mpd_map_active_height -> mpd_active_map_height() - DEPRECATED#endif //FLAG_MODE_MULTIDIR_SCROLL#if FLAG_MODE_MULTIDIR_SCROLL + FLAG_MODE_BIDIR_SCROLLMap scrolling step in the range 0..7
RW: u8 mpd_scroll_step_x [0..7] -> mpd_get_scroll_step_x()/mpd_set_scroll_step_x(...) - DEPRECATEDRW: u8 mpd_scroll_step_y [0..7] -> mpd_get_scroll_step_y()/mpd_set_scroll_step_y(...) - DEPRECATEDMap scrolling values:
R: s16 mpd_scroll_x -> mpd_scroll_x() - DEPRECATEDR: s16 mpd_scroll_y -> mpd_scroll_y() - DEPRECATED#endif //FLAG_MODE_MULTIDIR_SCROLL + FLAG_MODE_BIDIR_SCROLL*R-read only; RW-read/write-------------------------------------------- Replace the deprecated functions with the global variables. And don't write to read only variables. Updated info as usual here.All the changes and updated samples in the dev build.
|
|
|
Post by hyperfighting on Sept 8, 2022 16:36:49 GMT
Thanks very much for the advice. The next BIT (pun intended ) of refactoring will be to consolidate variables that can be represented as BITS! Thanks very much for this! I'm sure it will come in handy All of your changes are in place and working great! I did have one small complication this check no longer works...I was looking for an easy way to determine the Map was is at the end (can't scroll further). "if (mpd_scroll_x == __cropped_map_width)" Do you have a variable that I can use in place of "__cropped_map_width"? Thanks very much for the technique of isolating entities! I'm now able to cache X,Y coordinates! My codes look something like this... //INIT MAP
CACHE_ENTITY(16, 4, 24, 5); mpd_get_start_screen( &__scr_data, 5 );
if( mpd_find_entity_by_base_id( &__scr_data, 0 ) ) { Assign Player 1 x,y vars }
//CACHE ENTITY - Currently used for Enemies - Assumes __scr_data is global
void CACHE_ENTITY(unsigned char START_SLOT, unsigned char INC_SLOT, unsigned char END_SLOT, unsigned char MAP_IND) { u8 scr_h_n; u8 scr_w_n; unsigned char ent_n; actorID=START_SLOT;//Global Var mpd_init_screen_arr( &__scr_data, MAP_IND ); for( scr_h_n = 0; scr_h_n < mpd_map_scr_height; scr_h_n++ ) { for( scr_w_n = 1; scr_w_n < mpd_map_scr_width; scr_w_n++ )//Search screens beyond the first screen { mpd_get_screen_data( &__scr_data, scr_h_n * mpd_map_scr_width + scr_w_n ); //<-- the second argument calculates a screen index [0..N-1]
for( ent_n = 0; ent_n < __scr_data.scr.ents_cnt; ent_n++ ) { mpd_get_entity( &__scr_data, ent_n ); x[actorID]=__scr_data.inst_ent.inst.x_pos; y[actorID]=__scr_data.inst_ent.inst.y_pos; if ((actorID+=INC_SLOT)==END_SLOT) { return; } } } } }
|
|
|
Post by 0x8bitdev on Sept 9, 2022 7:58:06 GMT
All of your changes are in place and working great! I did have one small complication this check no longer works...I was looking for an easy way to determine the Map was is at the end (can't scroll further). "if (mpd_scroll_x == __cropped_map_width)" Do you have a variable that I can use in place of "__cropped_map_width"? That's cool if you found this global var in the library sources. In the post above: Active map width/height in pixels = map width/height in pixels - screen width/height in pixels
R: u16 mpd_map_active_width -> mpd_active_map_width() - DEPRECATED R: u16 mpd_map_active_height -> mpd_active_map_height() - DEPRECATED Thanks very much for the technique of isolating entities! I'm now able to cache X,Y coordinates! For whom do I write the documentation?.. This is a rhetorical question.
|
|
|
Post by hyperfighting on Sept 9, 2022 20:49:56 GMT
0x8bitdev - I know this thread is regarding MAPeD and SPReD but since you suggested a bit (pun intended...I know this is gonna get old fast lol) of bit refactoring; I figured I would share what I am using to consolidate some variables. As I understand it one BYTE is 8 bits and 0-15 can be stored in each half byte so with that said I am on a mission to take all variables that reside in the 0-15 range and consolidate. EX: I have two vars: unsigned char curr_frame[SLOT_ARRAY_SIZE]; unsigned char curr_frame_copy[SLOT_ARRAY_SIZE]; Both var's meet the criteria of never exceeding the value 15 so I am able to consolidate them to one variable. unsigned char curr_frame[SLOT_ARRAY_SIZE]; //ACTS AS "current frame" and "current frame copy values" to achieve this I am using these defines: //CLEAR LOWER BITS //ASSIGN VALUE #define A_CUR_FRAME(value) curr_frame[vramSlot]&=0xF0; curr_frame[vramSlot]|=value<<0; //CLEAR UPPER BITS //ASSIGN VALUE #define A_CUR_FRAME_COPY(value) curr_frame[vramSlot]&=0x0F; curr_frame[vramSlot]|=value<<4; //HIDE UPPER BITS #define CUR_FRAME (curr_frame[vramSlot]&0x0F) //HIDE LOWER BITS #define CUR_FRAME_COPY (curr_frame[vramSlot]>>4) //CLEAR UPPER BITS //ASSIGN "CUR_FRAME" // CLEAR LOWER BITS// ASSIGN "CUR_FRAME_COPY"+1 #define A_CUR_FRAME_ADD1 curr_frame[vramSlot]&=0x0F; curr_frame[vramSlot]|=CUR_FRAME<<4; curr_frame[vramSlot]&=0xF0; curr_frame[vramSlot]|=(CUR_FRAME_COPY+1)<<0;
//with these I can do this in the codez to set the vars
A_CUR_FRAME(8) //Set current frame var to 8
A_CUR_FRAME_COPY (CUR_FRAME) //Set the current frame copy to the value of current frame
if ((CUR_FRAME==1) && (CUR_FRAME_COPY==1)) //Test "Current Frame" and "Current Frame Copy" values
A_CUR_FRAME_ADD1 //Add 1 to the current Frame by using the "Current Frame Copy" as a reference point.
I have a bit of a better understanding of the bit level now ...I will do some similar stuff for flag values that are only ever 0-1 in that case 1 byte can be used for 8 variables! Hopefully someone reading finds this helpful. If I am off base and things can be written more efficiently please let me know. P.S. Thanks for pointing me toward "mpd_map_active_width" that did the trick! [upd] I have begun work on representing the flags as BITS.This is working for me just replace the bolded "0" with whatever BIT (0...7) you want to use as a flag unsigned char triggerSet1[SLOT_ARRAY_SIZE]; //CLEAR BIT 0 // ASSIGN BIT 0 "0-1" #define AB_ASSIGN_FLAG_TO_BIT0 (value) triggerSet1[vramSlot] = triggerSet1[vramSlot] & ~(1 << 0); triggerSet1[vramSlot]|=value<< 0; #define READ_BIT0_FLAG ((triggerSet1[vramSlot]>> 0)&1) One thing I am stuck on at this point is, is there a way to easily increment a nibble (4bits) EX: ++nibble?
|
|