Hi,
For Mortal Kombat, I want to implement blood splash and haduken fireballs without using sprites, but using instead basic effects that could be randomize a few (or not)
For performance, I think that I'll draw only one effect, in less than 1/2 vsync.
For blood, I can have 3 points of start, where player is hurt : top, middle, bottom.
I think that model could be a vector (two points : start and direction (length for importance))
Any suggestion of effect ?
Unless you draw simple points, generated effects will probably be slower than displaying sprites.
Here a 8Bytes hadouken :)
PS : I'm in mode 1, 1 color.
Source : point of impact, enter one per one ( max 8 )
At each step : ones from left go to right, more and more slowly. All gone from top to bottom at same speed.
Still 8 Bytes.
You can do this with small sprites, but you can also code your own specialized sprite code, like this:
ld (hl),<byte>
inc hl
ld (hl),<byte>
<move to the next line>
ld (hl),<byte>
dec hl
ld (hl),<byte>
etc.
This is as fast as you can get. I don't know how you manage your background, it should probably be stored/restored too.
https://www.youtube.com/watch?v=15Mb19-YrTg
Using algo, I can enter more or less blood...
In my humble opinion, it doesn't look very good. I would:
- either use blood sprites.
- use "particules". You could stick to the point (be pixel-accurate please), with a little X-speed (may be negative) and Y-speed, both would be initialized so that they all look a bit different. You could add more points according to the power of the hit. Use fixed-point number to manage accurate speeds. If you don't know how to do that, ask.
Quote from: Targhan on 22:10, 02 June 18
- use "particules". You could stick to the point (be pixel-accurate please), with a little X-speed (may be negative) and Y-speed, both would be initialized so that they all look a bit different. You could add more points according to the power of the hit. Use fixed-point number to manage accurate speeds. If you don't know how to do that, ask.
That's what I want, "particules" :)
My algorithm at this step is :
- insert pixel in algorithm one per one
- apply BLOOD_X_SPEED
- apply BLOOD_Y_SPEED
- resolve X pixels having same coordinates, pushing them X++
I added also :
- gravity (acceleration blood_g++ at each frame, started at 1)
- g (glitch - something stupid that accelerate new pixels more that old ones (g is local))
#define BLOOD_SIZE 14
//#define BLOOD_B_SPEED 0 - vitesse d'entrée des gouttes dans l'algo : déjà au maximum (en entrer plusieurs ?)
#define BLOOD_X_SPEED 8
#define BLOOD_Y_SPEED 4
unsigned char current_blood[BLOOD_SIZE][2];
char blood_depth=0;
char blood_n=0;
unsigned char blood_x_slow=0;
unsigned char blood_y_slow=0;
char blood_x;
char blood_y; // tete ou ventre ou pied
char blood_d; // direction
char blood_g; // gravite
/**
* Controler : propagation du sang (lancé à chaque frame)
*/
void blood() {
char i;char sx;char sy;char g;
if (blood_y==0) return;
g=0; // glitch (formule capturée lors de tests en échecs, car c'est jolie)
// ==> ou <== : on s'en fou, le tableau on le retournera à l'affichage !
blood_x_slow++;
if (blood_x_slow == BLOOD_X_SPEED) { // x
blood_x_slow=0;
}
if (blood_x_slow==0) {
for (i=0;i<blood_depth;i++) {
// se poussent en x
if (current_blood[i][0]<BLOOD_SIZE) { // x
current_blood[i][0]=current_blood[i][0]+1;
}
}
return;
}
blood_y_slow++;
if (blood_y_slow == BLOOD_Y_SPEED) { // y
blood_y_slow=0;
}
if (blood_y_slow==0) {
for (i=0;i<blood_depth;i++) {
if (current_blood[i][1]>0) { // y
// descend à vitesse constante.
current_blood[i][1]=current_blood[i][1]-1;
}
}
return;
}
// BLOOD_B_SPEED/blood_b_slow
if (blood_depth<blood_n) {
// insertion
current_blood[blood_depth][0]=0;
current_blood[blood_depth][1]=blood_y;
blood_depth++;
blood_g=1;
} else {
// gravité
for (i=1;i<blood_depth;i++) {
if (current_blood[i][1]>blood_g + g) {
// descend à vitesse non constante
current_blood[i][1]=current_blood[i][1]-g-blood_g;
g++;
} else {
// touche le sol
current_blood[i][1]=0;
}
}
blood_g++;
if (blood_g>7) {
// on coupe l'animation sang (car c'est moche sinon : une goutte reste bizarrement suspendue...)
blood_y=0;
blood_depth=0;
}
}
// solve superpositions X
sx=current_blood[0][0];sy=current_blood[0][1];
for (i=1;i<blood_depth;i++) {
if (current_blood[i][1] != sy) {
// nouvelle ligne, pas de problème de gouttes
sx=current_blood[i][0];
sy=current_blood[i][1];
} else if (current_blood[i][0] <= sx) {
// goutte génante, je la pousse, puis je la garde de côté
sx=sx+1;
current_blood[i][0]= sx;
} else {
// goutte pas génante, je la garde de côté
sx=current_blood[i][0];
}
}
}
/**
* Controler : lancé à la place de blood() lors d'un nouveau dégat - ça déclanche une nouvelle animation
* @param d : direction (plutôt aléatoire)
* @param n : dégats (entre 1 et BLOOD_SIZE)
* @param x,y : coordonnées du coup reçu
*/
void bloodDegats(char d, char n,char x, char y) {
blood_d = d;
blood_n = n;
blood_x = x;
blood_y = y;
blood_depth=0;
blood();
}
/**
* Renderer : Affiche le sang
*/
void bloodRender() {
char i;
for (i=0;i<blood_depth;i++) {
if (blood_d==0) {
if (blood_x+current_blood[i][0]>6*8+3+2) continue;
put_byte(blood_x+current_blood[i][0],120+50-1-current_blood[i][1],0xF0);
} else {
if (blood_x-current_blood[i][0]<3) continue;
put_byte(blood_x-current_blood[i][0],120+50-1-current_blood[i][1],0xF0);
}
}
}
/**
* Renderer : Efface le sang
*/
void bloodDerender() {
char i;
for (i=0;i<blood_depth;i++) {
if (blood_d==0) {
if (blood_x+current_blood[i][0]>6*8+3+2) continue;
put_byteC000(blood_x+current_blood[i][0],120+50-1-current_blood[i][1],0x00);
} else {
if (blood_x-current_blood[i][0]<3) continue;
put_byteC000(blood_x-current_blood[i][0],120+50-1-current_blood[i][1],0x00);
}
}
}
Well yes, it seems you already manages this somehow. But it doesn't look great, in my opinion. Be pixel-accurate, not byte-accurate.
https://www.youtube.com/watch?v=71QmiD5EvGg
I corrected the first launch of algorithm in order to enter more pixels at first step.
I do now launch several times blood() algorithm during first step :
#define BLOOD_SIZE 14
// 1 + 2 + 3 + 4 < 14
#define BLOOD_SIZE_INIT 5
(...)
void bloodDegats(char d, char n,char x, char y) {
(...)
while (blood_depth<blood_n && blood_depth<BLOOD_SIZE_INIT) {
blood();
}
and also enter pixels two per two :
if (blood_depth<blood_n) {
// insertion
current_blood[blood_depth][0]=0;
current_blood[blood_depth][1]=blood_y;
blood_depth++;
if (blood_depth<blood_n) {
current_blood[blood_depth][0]=1;
current_blood[blood_depth][1]=blood_y+1;
blood_depth++;
}
So normaly with BLOOD_SIZE_INIT == 5 relaunch of blood(), full blood does enter at first step of algorithm : 1 + 2 + 3 + 4 < BLOOD_SIZE/2
Quote#define BLOOD_SIZE 14
// 1 + 2 + 3 + 4 < 14
#define BLOOD_SIZE_INIT 5
//#define BLOOD_B_SPEED 0 - vitesse d'entrée des gouttes dans l'algo : déjà au maximum (en entrer plusieurs ?)
#define BLOOD_X_SPEED 8
#define BLOOD_Y_SPEED 4
unsigned char current_blood[BLOOD_SIZE][2];
char blood_depth=0;
char blood_n=0;
unsigned char blood_x_slow=0;
unsigned char blood_y_slow=0;
char blood_x;
char blood_y; // tete ou ventre ou pied
char blood_d; // direction
char blood_g; // gravite
/**
* Controler : propagation du sang (lancé à chaque frame)
*/
void blood() {
char i;char sx;char sy;char g;
if (blood_y==0) return;
g=0; // glitch (formule capturée lors de tests en échecs, car c'est jolie)
// ==> ou <== : on s'en fou, le tableau on le retournera à l'affichage !
blood_x_slow++;
if (blood_x_slow == BLOOD_X_SPEED) { // x
blood_x_slow=0;
}
if (blood_x_slow==0) {
for (i=0;i<blood_depth;i++) {
// se poussent en x
if (current_blood- <BLOOD_SIZE) { // x
current_blood- =current_blood
- +1;
}
}
return;
}
blood_y_slow++;
if (blood_y_slow == BLOOD_Y_SPEED) { // y
blood_y_slow=0;
}
if (blood_y_slow==0) {
for (i=0;i<blood_depth;i++) {
if (current_blood[1]>0) { // y
// descend à vitesse constante.
current_blood[1]=current_blood[1]-1;
}
}
return;
}
// BLOOD_B_SPEED/blood_b_slow
if (blood_depth<blood_n) {
// insertion
current_blood[blood_depth][0]=0;
current_blood[blood_depth][1]=blood_y;
blood_depth++;
if (blood_depth<blood_n) {
current_blood[blood_depth][0]=1;
current_blood[blood_depth][1]=blood_y+1;
blood_depth++;
}
blood_g=1;
} else {
// gravité
for (i=1;i<blood_depth;i++) {
if (current_blood[1]>blood_g + g) {
// descend à vitesse non constante
current_blood[1]=current_blood[1]-g-blood_g;
g++;
} else {
// touche le sol
current_blood[1]=0;
}
}
blood_g++;
if (blood_g>7) {
// on coupe l'animation sang (car c'est moche sinon : une goutte reste bizarrement suspendue...)
blood_y=0;
blood_depth=0;
}
}
// solve superpositions X
sx=current_blood[0][0];sy=current_blood[0][1];
for (i=1;i<blood_depth;i++) {
if (current_blood[1] != sy) {
// nouvelle ligne, pas de problème de gouttes
sx=current_blood- ;
sy=current_blood[1];
} else if (current_blood- <= sx) {
// goutte génante, je la pousse, puis je la garde de côté
sx=sx+1;
current_blood- = sx;
} else {
// goutte pas génante, je la garde de côté
sx=current_blood- ;
}
}
}
/**
* Controler : lancé à la place de blood() lors d'un nouveau dégat - ça déclanche une nouvelle animation
* @param d : direction (plutôt aléatoire)
* @param n : dégats (entre 1 et BLOOD_SIZE)
* @param x,y : coordonnées du coup reçu
*/
void bloodDegats(char d, char n,char x, char y) {
blood_d = d;
blood_n = n;
blood_x = x;
blood_y = y;
blood_depth=0;
while (blood_depth<blood_n && blood_depth<BLOOD_SIZE_INIT) {
blood();
}
}
/**
* Renderer : Affiche le sang
*/
void bloodRender() {
char i;
for (i=0;i<blood_depth;i++) {
if (blood_d==0) {
if (blood_x+current_blood- >6*8+3+2) continue;
put_byte(blood_x+current_blood- ,120+50-1-current_blood[1],0xF0);
} else {
if (blood_x-current_blood- <3) continue;
put_byte(blood_x-current_blood- ,120+50-1-current_blood[1],0xF0);
}
}
}
/**
* Renderer : Efface le sang
*/
void bloodDerender() {
char i;
for (i=0;i<blood_depth;i++) {
if (blood_d==0) {
if (blood_x+current_blood- >6*8+3+2) continue;
put_byteC000(blood_x+current_blood- ,120+50-1-current_blood[1],0x00);
} else {
if (blood_x-current_blood- <3) continue;
put_byteC000(blood_x-current_blood- ,120+50-1-current_blood[1],0x00);
}
}
}
Better, but still not great. Make your blood Speed Y be faster maybe, so that it can move instead of "flying" at remaining at the same height before disappearing. Sorry I won't have time to check your code.
I take note of that.
I started implementing also Hadouken (but without damages), and start export/import of player parameters.
I think I'll do a second pass on my code. I've got a lot of small things to calibrate/finish.
blood in walls...
Quote from: Targhan on 14:26, 28 May 18
You can do this with small sprites, but you can also code your own specialized sprite code, like this:
ld (hl),<byte>
inc hl
ld (hl),<byte>
<move to the next line>
ld (hl),<byte>
dec hl
ld (hl),<byte>
etc.
This is as fast as you can get. I don't know how you manage your background, it should probably be stored/restored too.
Re: this is as fast as you can get; if you suitably aligned and sized your display surely you could get away with just inc/dec l?
Very interesting thread. May I suggest that you use pixel dots instead of the lines? Also - a lesser amount too?Have you tried changing the colour of the lines while they are animating? If you cycle from Pink - Red - Dark Red - Black, that could give a better interpretation of the blood disappearing.
Too late : I do release :p
http://www.youtube.com/watch?v=t1RK0jXg9Uo
Quote from: sigh on 13:49, 27 June 18
Very interesting thread. May I suggest that you use pixel dots instead of the lines? Also - a lesser amount too?Have you tried changing the colour of the lines while they are animating? If you cycle from Pink - Red - Dark Red - Black, that could give a better interpretation of the blood disappearing.
I'm using here mode 1, so only 4 colors : blood, player1, background, player2.