Author Topic: Danmakufu Q&A/Problem Thread  (Read 119034 times)

Danmakufu Q&A/Problem Thread
« on: May 02, 2009, 05:36:16 AM »
Hey, I just decided I'd make a place where people can ask questions and post their problems, hopefully to be answered.

I might be able to answer some, but first, I have a question of my own. What's the difference between a sub, a task, and a function? they seem to be used in similar ways.

Re: Danmakufu Q&A/Problem Thread
« Reply #1 on: May 02, 2009, 06:18:12 AM »
In order of processing speed, from fastest to slowest:

sub
function
task

And here's why. A sub is just a routine that you often call that requires no changing whatsoever. So if I wanted to make the same array of shots happen over and over again, but at different places in the script, I could say somewhere in script_enemy_main:

Code: [Select]
sub bullatz{
CreateShot01(GetX, GetY, 3, GetAngleToPlayer - 30, RED02, 0);
CreateShot01(GetX, GetY, 3, GetAngleToPlayer - 20, RED02, 0);
CreateShot01(GetX, GetY, 3, GetAngleToPlayer - 10, RED02, 0);
CreateShot01(GetX, GetY, 3, GetAngleToPlayer, RED02, 0);
CreateShot01(GetX, GetY, 3, GetAngleToPlayer + 10, RED02, 0);
CreateShot01(GetX, GetY, 3, GetAngleToPlayer + 20, RED02, 0);
CreateShot01(GetX, GetY, 3, GetAngleToPlayer + 30, RED02, 0);
}

And then say bullatz; anywhere in my script and those shots will be fired.

A function is the same thing, except you can edit parameters of the shots as you're calling them. So, to fire a similar array of shots:

Code: [Select]
function bullutz(angle){
CreateShot01(GetX, GetY, 3, angle - 30, RED02, 0);
CreateShot01(GetX, GetY, 3, angle - 20, RED02, 0);
CreateShot01(GetX, GetY, 3, angle - 10, RED02, 0);
CreateShot01(GetX, GetY, 3, angle, RED02, 0);
CreateShot01(GetX, GetY, 3, angle + 10, RED02, 0);
CreateShot01(GetX, GetY, 3, angle + 20, RED02, 0);
CreateShot01(GetX, GetY, 3, angle + 30, RED02, 0);
}

This time, I'll call bullutz(90); to shoot this array of bullets with the updated angle of 90 degrees. I could say bullutz(270); for another direction. And that's what functions are in Danmakufuland. Ah, you can also call more than one paramter in a function, so don't feel limited by only one independent variable.

A task, as you might guess, is an upgraded function, and thus takes the most processing power. With tasks you are basically creating another @MainLoop that you control whenever it's run. To do this, tasks use the magical operator called yield;. yield; allows you to freely switch between processing your task and processing your @MainLoop, so you can list ordered events, and have them only carry out your actions once without repeat, and still edit them. yield; suspends the current task or MainLoop and then carries onto another task until it hits another yield; command, then it goes back to the MainLoop and finishes the frame cycle. It'll continue where it left off the next frame, which makes it very easy to list ordered events. A task is basically a list of events that you want to happen, that you can freely edit at anytime during the script using the same parameters as a function.

And an example of a task:

Code: [Select]
task attack(delay, angle){
CreateShot01(GetX, GetY, 5, GetAngleToPlayer, RED03, delay);
loop(120){yield;}
loop(5){
  CreateShot01(GetX, GetY, 3, GetAngleToPlayer, BLUE01, 0);
  loop(5){yield;}
  }
CreateShot01(GetX, GetY, 2, angle, YELLOW12, 0);
}

So now, I'll call attack(5, 90); somehwere in @MainLoop to fire of a large red shot towards the player with 5 delay, the script will wait two seconds (120 frames, the loop(120){yield;} statement), then fire 5 blue shots in succession but wait 5 frames inbetween each shot, and then finally firing a small yellow shot at 90 degrees.

It should be noted that you MUST include yield; somewhere in your @MainLoop (preferably at the beginning or end, easier to keep track of) and call it every frame to ensure that the yield; command works properly in your tasks.


Anyways, you probably already knew about yield; and crap, but I thought I would be clear with the explanation and include everything, even though this is a very poor description of the yield; command and what it does.

Re: Danmakufu Q&A/Problem Thread
« Reply #2 on: May 03, 2009, 12:13:11 AM »
I'll contribute with my own question now, I suppose. Does GetAngleToPlayer output a value between 0 and 360, a value between -180 and 180, or something else? Does atan2() have the same output parameters?
« Last Edit: May 03, 2009, 12:14:48 AM by Naut »

Drake

  • *
Re: Danmakufu Q&A/Problem Thread
« Reply #3 on: May 03, 2009, 04:38:37 AM »
There's a way to check. Spawn a bullet with the delay time GetAngleToPlayer, but stand in the upper right corner. Similarly you could set it to atan2(player-boss, player-boss).

Obviously if you get an error it's a negative value.

A Colorful Calculating Creative and Cuddly Crafty Callipygous Clever Commander
- original art by Aiけん | ウサホリ -

Stuffman

  • *
  • We're having a ball!
Re: Danmakufu Q&A/Problem Thread
« Reply #4 on: May 03, 2009, 04:48:43 AM »
As far as I know whenever the engine outputs an angle it's in the -180 to 180 range, even though it accepts any degree amount you put in.

Garlyle

  • I can't brain today
  • I have the dumb
    • Tormod Plays Games
Re: Danmakufu Q&A/Problem Thread
« Reply #5 on: May 03, 2009, 04:07:49 PM »
Quickest way I can think of: Base script, boss moves to center of screen, and have a DrawText string that writes the GetAngleToPlayer onto the screen somewhere so you can check it.

I did the same when trying to test out damage originally: A boss script that, once its invincibility wore off, counted the frames that passed until you beat it.  Since it didn't move, it let me test damage in terms of how many frames it took to take down ~1000 HP.

Re: Danmakufu Q&A/Problem Thread
« Reply #6 on: May 03, 2009, 05:44:04 PM »
Figures. Here's the test results:






GetAngleToPlayer outputs an angle between 0 and 360, atan2 outputs an angle between -180 and 180. Why is this not surprising...?

Sigh, thanks Danmakufu.

Interestingly enough, the angles are not the same, so my thought that GetAngleToPlayer = atan2(GetPlayerY - GetY, GetPlayerX - GetX) seems to be incorrect, even if the angle has 360 added to it if it outputs a negative value (notice the decimal value...? Not cool). They are negligably different though, so it doesn't really make much of a difference in a script. But really, what the hell.
« Last Edit: May 03, 2009, 05:52:05 PM by Naut »

Drake

  • *
Re: Danmakufu Q&A/Problem Thread
« Reply #7 on: May 03, 2009, 07:54:20 PM »
atan2 is probably less accurate considering Danmakufu does math with rounded decimals based on pixel-perfect locations.

Although if that's how it works, I wonder how GetAngle works...?

A Colorful Calculating Creative and Cuddly Crafty Callipygous Clever Commander
- original art by Aiけん | ウサホリ -

Re: Danmakufu Q&A/Problem Thread
« Reply #8 on: May 05, 2009, 12:58:02 AM »
I've got a question.


1) Currently, I am messing with the curve laser function (CreateLaserC/SetLaserDataC). I place an AddShot (CreateShotA) to the lasers.

However, I add the bullets to the lasers, but every single bullet added to a laser goes in one direction (i.e. each curved laser goes in different directions, but every other bullet flies in the direction of, for example, 0 degrees). In addition, I am only looping one laser to add each of the bullets (one laser is looped five times in equal distances apart, the AddShot bullets are also looped to the one laser).

Here is the code I am trying to work with.

Code: [Select]
#TouhouDanmakufu
#Title[Winter Sign "Flower Wither Away"]
#Text[Test script]
#Image[.\img\Scarletsign.png]
#Player[FREE]
#ScriptVersion[2]

script_enemy_main {
let ImgBoss = "script\thC\IMAGE\CHAR\DOT\boss_remilia.png";

let count = 0;
let a = 72;

@Initialize {
SetLife(2500);
SetTimer(60);
SetScore(10000000);

SetMovePosition02(225, 120, 60);
CutIn(YOUMU, "Winter Sign "\""Flower Wither Away"\", "", 0, 0, 0, 0);

LoadGraphic(ImgBoss);
SetTexture(ImgBoss);
SetGraphicRect(0, 0, 128, 128);
}

@MainLoop {
SetCollisionA(GetX, GetY, 32);
SetCollisionB(GetX, GetY, 16);

if(count == 90){
PlaySE("lib\THCCL\SE\lazer00.wav");
loop(5){
CreateLaserC(1, GetX, GetY, 10, 30, WHITE01, 0);
SetLaserDataC(1, 0, 5, a, 4, 0, 3);
SetLaserDataC(1, 50, NULL, NULL, 5, -0.05, 2);
SetLaserDataC(1, 90, NULL, NULL, 0, 0.05, 5);
a += 360/5;

ascent(i in 1..900){
    CreateShotA(2, 0, 0, 20);
    SetShotDataA(2, 0, 1, 30, 0, 0, 1, BLUE11);
    CreateShotA(3, 0, 0, 20);
    SetShotDataA(3, 0, 1, 180, 0, 0, 1, BLUE11);
    CreateShotA(4, 0, 0, 20);
    SetShotDataA(4, 0, 1, -30, 0, 0, 1, BLUE11);
    AddShot(i*6+60, 1, 2, 0);
    AddShot(i*6+61, 1, 3, 0);
    AddShot(i*6+61, 1, 4, 0);
}

FireShot(1);
}
}

if(count == 210){
PlaySE("lib\THCCL\SE\lazer00.wav");
loop(5){
CreateLaserC(5, GetX, GetY, 10, 30, WHITE01, 0);
SetLaserDataC(5, 0, 5, a, -4, 0, 3);
SetLaserDataC(5, 50, NULL, NULL, -5, -0.05, 2);
SetLaserDataC(5, 90, NULL, NULL, 0, 0.05, 5);
a += 360/5;

ascent(i in 1..900){
    CreateShotA(6, 0, 0, 20);
    SetShotDataA(6, 0, 1, 30, 0, 0, 1, BLUE11);
    CreateShotA(7, 0, 0, 20);
    SetShotDataA(7, 0, 1, 180, 0, 0, 1, BLUE11);
    CreateShotA(8, 0, 0, 20);
    SetShotDataA(8, 0, 1, -30, 0, 0, 1, BLUE11);
    AddShot(i*6+60, 5, 6, 0);
    AddShot(i*6+61, 5, 7, 0);
    AddShot(i*6+61, 5, 8, 0);
}

FireShot(5);
}
;
count=0
}
count++;
}

@DrawLoop {
DrawGraphic(GetX, GetY);
}

@Finalize {
DeleteGraphic(ImgBoss);
}
}


If you notice, I am trying to make a spellcard at least similar to Letty Whiterock's second spellcard of PCB. She creates five curved lasers. However, every one of the AddShot bullets in its respective I.D. only go in one direction. I am not quite sure how to make this easy to explain, but I would like each bullet to get the angle of its respective laser, not just the single core point.

Is it possible to do this with only one laser looped five times, or do I have to create separate lasers and bullets to make it work like Letty's? I want to keep the code as simple as possible.

Re: Danmakufu Q&A/Problem Thread
« Reply #9 on: May 05, 2009, 10:26:48 PM »
I've got a question.


1) Currently, I am messing with the curve laser function (CreateLaserC/SetLaserDataC). I place an AddShot (CreateShotA) to the lasers.

However, I add the bullets to the lasers, but every single bullet added to a laser goes in one direction (i.e. each curved laser goes in different directions, but every other bullet flies in the direction of, for example, 0 degrees). In addition, I am only looping one laser to add each of the bullets (one laser is looped five times in equal distances apart, the AddShot bullets are also looped to the one laser).

Here is the code I am trying to work with.

Code: [Select]
#TouhouDanmakufu
#Title[Winter Sign "Flower Wither Away"]
#Text[Test script]
#Image[.\img\Scarletsign.png]
#Player[FREE]
#ScriptVersion[2]

script_enemy_main {
let ImgBoss = "script\thC\IMAGE\CHAR\DOT\boss_remilia.png";

let count = 0;
let a = 72;

@Initialize {
SetLife(2500);
SetTimer(60);
SetScore(10000000);

SetMovePosition02(225, 120, 60);
CutIn(YOUMU, "Winter Sign "\""Flower Wither Away"\", "", 0, 0, 0, 0);

LoadGraphic(ImgBoss);
SetTexture(ImgBoss);
SetGraphicRect(0, 0, 128, 128);
}

@MainLoop {
SetCollisionA(GetX, GetY, 32);
SetCollisionB(GetX, GetY, 16);

if(count == 90){
PlaySE("lib\THCCL\SE\lazer00.wav");
loop(5){
CreateLaserC(1, GetX, GetY, 10, 30, WHITE01, 0);
SetLaserDataC(1, 0, 5, a, 4, 0, 3);
SetLaserDataC(1, 50, NULL, NULL, 5, -0.05, 2);
SetLaserDataC(1, 90, NULL, NULL, 0, 0.05, 5);
a += 360/5;

ascent(i in 1..900){
    CreateShotA(2, 0, 0, 20);
    SetShotDataA(2, 0, 1, 30, 0, 0, 1, BLUE11);
    CreateShotA(3, 0, 0, 20);
    SetShotDataA(3, 0, 1, 180, 0, 0, 1, BLUE11);
    CreateShotA(4, 0, 0, 20);
    SetShotDataA(4, 0, 1, -30, 0, 0, 1, BLUE11);
    AddShot(i*6+60, 1, 2, 0);
    AddShot(i*6+61, 1, 3, 0);
    AddShot(i*6+61, 1, 4, 0);
}

FireShot(1);
}
}

if(count == 210){
PlaySE("lib\THCCL\SE\lazer00.wav");
loop(5){
CreateLaserC(5, GetX, GetY, 10, 30, WHITE01, 0);
SetLaserDataC(5, 0, 5, a, -4, 0, 3);
SetLaserDataC(5, 50, NULL, NULL, -5, -0.05, 2);
SetLaserDataC(5, 90, NULL, NULL, 0, 0.05, 5);
a += 360/5;

ascent(i in 1..900){
    CreateShotA(6, 0, 0, 20);
    SetShotDataA(6, 0, 1, 30, 0, 0, 1, BLUE11);
    CreateShotA(7, 0, 0, 20);
    SetShotDataA(7, 0, 1, 180, 0, 0, 1, BLUE11);
    CreateShotA(8, 0, 0, 20);
    SetShotDataA(8, 0, 1, -30, 0, 0, 1, BLUE11);
    AddShot(i*6+60, 5, 6, 0);
    AddShot(i*6+61, 5, 7, 0);
    AddShot(i*6+61, 5, 8, 0);
}

FireShot(5);
}
;
count=0
}
count++;
}

@DrawLoop {
DrawGraphic(GetX, GetY);
}

@Finalize {
DeleteGraphic(ImgBoss);
}
}


If you notice, I am trying to make a spellcard at least similar to Letty Whiterock's second spellcard of PCB. She creates five curved lasers. However, every one of the AddShot bullets in its respective I.D. only go in one direction. I am not quite sure how to make this easy to explain, but I would like each bullet to get the angle of its respective laser, not just the single core point.

Is it possible to do this with only one laser looped five times, or do I have to create separate lasers and bullets to make it work like Letty's? I want to keep the code as simple as possible.

Unfortunately, I don't think there's any way to track the angle of LaserC. Let me know if I'm wrong. You might have to use Object Lasers in order to be able to track the angle of it, and then fire the shots relative to that (or something along those lines..) .

I have another question. How do you make bullet patterns similar to Alice's PCB noncards?

Re: Danmakufu Q&A/Problem Thread
« Reply #10 on: May 05, 2009, 10:41:30 PM »
Haven't the slightest idea what to do about those curved lasers, don't mess with them myself. Sorry.

In reference to your question Darkblizer, which patterns are we looking at specifically? Her third one looks probably the hardest to code, but the rest of them shouldn't be too hard to throw together, relatively speaking. Mind telling us which one you're looking at?

Re: Danmakufu Q&A/Problem Thread
« Reply #11 on: May 05, 2009, 11:43:40 PM »
I was talking about her first and fourth noncard.

Re: Danmakufu Q&A/Problem Thread
« Reply #12 on: May 06, 2009, 12:17:37 AM »
Hmm, I was gonna just recreate them, but the values are a bit odd and I don't want to spend much time on this, so I'll give you the basic outline of what you could go about doing.

For her first non-card you'll want to create shots in a circle around her using [GetX + radius*cos(angle), GetY + radius*sin(angle)]. Angle will be constantly incrementing or decrementing, depending on if you want the bullets to be spawned in a clockwise or counter-clockwise circle, respectively. You'll want to decrease radius everytime you spawn a bullet, to give the same effect observed for her first non-card.

For the actual bullets, it looks like ZUN sets the to have a bit of an angular velocity at first, so we'll be dealing with CreateShotA's. loop(4) (with an angle+=360/4 inside the loop, you know the drill) of them, make sure you don't use the same id twice. Give them a slight negative (for counter clockwise movement) or positive (for clockwise) angular velocity (<1), and spawn four on each point (so, you're looping 4 instances of four different CreateShotA's, so 16 bullets will be spawned). Two of the bullets are just the exact same thing with a larger delay (10 vs 30, maybe), one of the bullets will have a higher velocity and be shot at a different angle (just change the angle parameter to angle + 30, or something), and the other bullet will just be shot at another different angle, with another different velocity. Repeat for all four of the colours, incrementing and decreaming angle and radius as necessary. Be sure to reset your values everytime you start another loop.

Taking a look at her fourth non-card now. Let me know if I wasn't clear.

Alright, for her fourth non-card, you'll want to loop(12) of loop(36) with bullets in them. CreateShotA's again, because of varying movement. The loop(36) is for the ring of bullets, loop(12) is how many rings you'll want to spawn. In the loop(36), you'll want two bullets, your RED01 shot, which will just be fired off, accellerate, then slow down again, and your RED21 shot, which will be fired off, negatively accelerate and continue onward (you'll actually want it to fire off at a negative velocity, so when you accelerate it backwards the bullet graphic will be pointing the correct direction). You'll be incrementing the angle in loop(36) for your ring, and in the loop(12) you'll want to increment velocity so that they'll be at different points (giving that expanding effect).

Instead of incrementing velocity, you could try incrementing delay, but I tried this and it didn't yield the correct results, or anything close really. But you might have more luck. Angular velocity seemed to play a slight role as well, but I couldn't get anything to work that I was happy with. Again, you can try it if you feel like it, but it's a lot of math for a very small effect.

Both of these patterns require you to test and retest the values to mimic them correctly, but since you're just looking for the general pattern, I should hope there wouldn't be a need for me to completely recreate the patterns as a guide. Anyways, I hope this has been helpful to you, and I hope to God I'm talking about the right non-cards. Eeep!
« Last Edit: May 06, 2009, 12:54:11 AM by Naut »

Re: Danmakufu Q&A/Problem Thread
« Reply #13 on: May 07, 2009, 11:33:27 PM »
Okay, I sort of need help figuring out on how to do spellcards where you need to find a gap in the pattern (virtue of wind god is one) to win.

Edit: And those sort of loop thingys Koishi does?
« Last Edit: May 07, 2009, 11:37:57 PM by Darkblizer »

Re: Danmakufu Q&A/Problem Thread
« Reply #14 on: May 08, 2009, 12:30:05 AM »
Good luck with creating gaps in danmaku, it requires a Godly knowledge of mathematics to calculate, or a Godly amount of luck to pull off. Probably an inordinate amount of both. So I can give one tip that you can try. You'll want to spawn very thick rings of bullets, but with holes in them (this can be done by skipping the spawning stage of bullets but still adding the angle counter +=360/x, so it will make a hole in the ring of bullets. How you go about doing this varies depending on your style, so you can dablle away at it). You can spawn them at random or predetermined angles, but give them an angular velocity. This way it will spin around with seemingly no opens, but if you can math correctly, you can get some openings to appear as they get closer to the bottom of the stage. This is about all I can say, that kind of danmaku is really hard to make unless you know exactly what your doing... Or, y'know, the luck.

Alrighty, loopy things... Spellcard-loopy things or non-spellcard? Erm, closest I can think of is her "Rorschach in Danmaku" or "Genetics of the Subconcious". Honestly? I haven't much of the slightest. All I can say is it requires a knowledge of sin, cos and atan2 that I don't quite possess. So, this is the best I can do for you.

Behold, code!

Code: [Select]
#TouhouDanmakufu
#Title[Rorschach]
#Text[Math]
#Player[FREE]
#ScriptVersion[2]

script_enemy_main {

///////////////////////////
///////////////////////////
///////////////////////////
///////////////////////////

let ImgBoss = "script\ExRumia\img\ExRumia.png";

    @Initialize {

    SetLife(5000);
SetScore(5000000);
SetTimer(60);
SetEnemyMarker(true);
    SetDamageRate(100, 10);
    SetInvincibility(150);
SetEnemyMarker(true);
    LoadGraphic(ImgBoss);
CutIn(YOUMU, "Rorschach", "", 0, 0, 0, 0);
    SetMovePosition02(GetCenterX, GetClipMinY + 160, 1);
    SetGraphicRect(30, 30, 100, 100);

lissajous; //attack.

    }

    @MainLoop {
    SetCollisionA(GetX, GetY, 32);
    SetCollisionB(GetX, GetY, 16);

yield;

    }
    @DrawLoop {
SetTexture(ImgBoss);
if(GetSpeedX()==0){SetGraphicRect(64,1,127,64);}
else if(GetSpeedX()>0){SetGraphicRect(192,1,255,64);}
else if(GetSpeedX()<0){SetGraphicRect(128,1,191,64);}
DrawGraphic(GetX, GetY);
    }

    @Finalize {
DeleteGraphic(ImgBoss);
    }

function wait(k){
loop(k){yield;}
}

task lissajous {
let x = GetX;
let y = GetY + 170;
let x1 = 0;
let y1 = 0;
let ex1 = 0;
let ey1 = 0;
let x2 = 0;
let y2 = 0;
let ex2 = 0;
let ey2 = 0;

loop(2) {
let t1 = -90;
let t2 = -90;
loop(2) {
loop(78) {
x1 = x + cos(t1) * 100;
y1 = y + cos(t1 + 30) * 100;
ex1 = x + cos(t2) * 10000;
ey1 = y + sin(t2) * 10000;
x2 = x + cos(t2) * 100;
y2 = y + cos(t2 + 150) * 100;
ex2 = x + cos(t1) * 10000;
ey2 = y + sin(t1) * 10000;

CreateShotA(0, x1, y1, 22);
SetShotDataA(0, 0, 0, atan2(ey1 - y1, ex1 - x1) + 30, 0, 0, 0, BLUE01);
SetShotDataA(0, 56, 0, NULL, 0, 0.1, 2, BLUE04);
FireShot(0);

CreateShotA(0, x2, y2, 22);
SetShotDataA(0, 0, 0, atan2(ey2 - y2, ex2 - x2) - 30, 0, 0, 0, BLUE01);
SetShotDataA(0, 56, 0, NULL, 0, 0.1, 2, BLUE04);
FireShot(0);

t1 -= 4.6;
t2 += 4.6;

wait(1);
}
wait(1);
}
}

loop(2) {
let t1 = -90;
let t2 = -90;
loop(2) {
loop(78) {
x1 = x + cos(t1) * 100;
y1 = y + cos(t1 + 30) * 50;
ex1 = x + cos(t2) * 10000;
ey1 = y + sin(t2) * 10000;
x2 = x + cos(t2) * 100;
y2 = y + cos(t2 + 150) * 50;
ex2 = x + cos(t1) * 10000;
ey2 = y + sin(t1) * 10000;

CreateShotA(0, x1, y1, 22);
SetShotDataA(0, 0, 0, atan2(ey1 - y1, ex1 - x1) + 30, 0, 0, 0, GREEN01);
SetShotDataA(0, 56, 0, NULL, 0, 0.1, 2, GREEN04);
FireShot(0);

CreateShotA(0, x2, y2, 22);
SetShotDataA(0, 0, 0, atan2(ey2 - y2, ex2 - x2) - 30, 0, 0, 0, GREEN01);
SetShotDataA(0, 56, 0, NULL, 0, 0.1, 2, GREEN04);
FireShot(0);

t1 -= 4.6;
t2 += 4.6;

wait(1);
}
wait(1);
}
}
}
}

Run it, study it, become it!

I am too!

Re: Danmakufu Q&A/Problem Thread
« Reply #15 on: May 08, 2009, 01:48:37 AM »
For creating gaps in ring bullet attacks, it is not all that difficult if I am thinking about what you are talking about.

To do this, when you loop the number of bullets and add "[variable] += #/[number of loop]", do not input # as 360. Instead, try for less, such as 340, 300, 180, etc.

If you want the placement of the gap to stay the same, you will need to put "[variable] = [angle]", where angle = the placement of the gap, outside of the loop bracket. This way, the gap will always be opening in one direction.

If you want to randomize the placement of the gap, simply change the # in the first statement to rand(min. angle, max. angle). You can toy with those values as you please.

If you want randomized and MULTIPLE openings, I cannot help you there right now. Sorry.

Re: Danmakufu Q&A/Problem Thread
« Reply #16 on: May 08, 2009, 01:55:23 AM »
Oh, my bad, I immediately thought he meant multiple meshing gaps, as per Virute of Wind God, didn't even think about just single gaps. Yeah, Frazer's mothod work better than mine anyway, listen to him.

Infy♫

  • Demonic★Moe
  • *
Re: Danmakufu Q&A/Problem Thread
« Reply #17 on: May 11, 2009, 05:27:01 PM »
soooo I'm re-making the scarlet devil mansion's hallways in danmakufu... here's a screenshot:

it probably doesnt look very good but that's not the problem. the fog does not work the way it should. It would really look nicer if it did. but the results I'm getting are bad. is it danmakufu or just a wrong thing? this is what I use:       SetFog(800, 3200, 150, 50, 50);
what i get with the fog effect:

note: everything is tiled and drawn somewhere close to 0,0,0.
you might not find the code very useful but anyways: here is it:
Code: [Select]
      rotation++;
      WriteZBuffer(true);
      UseZBuffer(true);
      SetViewTo(200 , 0 , sin(rotation /2) *50  );
      SetViewFrom( 800, 0, 50);
      SetAlpha(255);
      SetTexture(imagefile8);
      SetGraphicAngle(90, 90, 0);
      SetGraphicRect(0 , 0, 67 * 4000, 35 * 4000);
      DrawGraphic3D(rotation, 0, -100);
      SetTexture(imagefile10);
      SetGraphicScale(3,3);
      SetGraphicAngle(90, 90, 0);
      SetRenderState(ADD);
      SetGraphicRect(0 , 0, 66, 57* 10000);
      DrawGraphic3D( rotation, 5 , -50);
      SetAlpha(150);
      SetRenderState(ALPHA);
      SetTexture(imagefile9);
      SetGraphicScale(1,1);
      SetGraphicAngle(90, 90, 90);
      SetGraphicRect(0 , 0, 78 *20000, 43);
      DrawGraphic3D( rotation, 50 , 220);
      DrawGraphic3D( rotation, 50 , -220-35);
      DrawGraphic3D( rotation, 200 , 220);
      DrawGraphic3D( rotation, 200 , -220-35);
      DrawGraphic3D( rotation, 125 , 220);
      DrawGraphic3D( rotation, 125 , -220-35);
      DrawGraphic3D( rotation, 275 , 220);
      DrawGraphic3D( rotation, 275 , -220 -35);
      SetGraphicAngle(90, 90, 270);
      DrawGraphic3D( rotation, 50 , 220 +35);
      DrawGraphic3D( rotation, 50 , -220);
      DrawGraphic3D( rotation, 200 , 220+35);
      DrawGraphic3D( rotation, 200 , -220);
      DrawGraphic3D( rotation, 125 , 220+35);
      DrawGraphic3D( rotation, 125 , -220);
      DrawGraphic3D( rotation, 275 , 220+35);
      DrawGraphic3D( rotation, 275 , -220);

Aphid

Re: Danmakufu Q&A/Problem Thread
« Reply #18 on: May 11, 2009, 06:41:07 PM »
Note: You can't copy this code and expect it to work. You'll have to fill in the variables or have them defined in your script as a result of things (say getplayerX,Y, etc.)

A solution to making multiple gaps into a bullet circle.

Non-Array Variables used:

a, b ==> Angles of our bullet circle (starting and ending, thus a full circle would use 0, 360. It's vital that b > a, and that b, a are within the set (0,360). If not some problems occur with calculating correct lengths and positions. To get these values out of larger angles, use a modulus(360).
n ==> Amount of bullets in the circle.
v ==> Speed of the circle
a ==> Accelleration of the circle.
av ==> Angular velocity of bullets in the circle. Due to the nature of the circle it will always remain as such. A higher angular velocity will slow down the expansion of the circle limiting it to the size of a circle in which a bullet with angular velocity av will rotate, and also of course rotate the gap. Careful use can lead to interesting danmaku.
dur ==> Duration for which the angular velocity applies. I always set one so that there's never stray bullets. Of course there's other removal functions but having the bullets glide off the screen by themselves has preference. As long as v =/= 0 and the amount of bullets isn't too large it's okay to do it this way.
i, j, k, l==> Used to count.
m ==> Amount of gaps.
X, Y ==> Center Position of the ring.

3 arrays:

EXST (length n) ==> Used within the code itself.
l (length m)==> Length of all the gaps (in degrees).
p (length m)==> Starting position of all the gaps (in degrees).

The code consists of three sections. The first section a very basic thing you'll need to make it work in your script, the second section one to determine the EXST from the other variables, and the third one actual bullet fire script. Well let's see:

Code: [Select]

let EXST = [1,1,1,1,(...),1,1,1];
let l = [l1, l2, l3, (...), lm];
let lb = [0, 0, 0, (...), 0];
\\where of course l1, l2, are things I don't know, but you obviously have some things in mind...
let p = [p1, p2, p3, (...), pm];
let pb = [0, 0, 0, (...), 0];
\\ note that the lengths of l, lb, p, and pb are all m.


This should be in @initialize and at least the first line also somewhere in @mainloop. Each loop it resets EXST so that it can be used again. Alternatively, you can place it right after EXST is read out for the last time in a set of bullet rings so you can use it again when it comes around. Of course the other variables also need defining. Anyway, on to the script for EXST. First we create lb out of l, so we converge the angular lengths into lengths specified by the amount of bullets they contain, the same with the positions p.:

Code: [Select]

ascent(i in 1..n+1) {
let lb[i] = round(l[i] / (b - a) * n);
let pb[i] = round((p[i] - a) / (b - a) * n);
                    }

Next, we now determine our EXST, or 'EXIST'-variable, the one that determines which bullets should exist and which don't. By default, all exist (equal to '1'). And a diffrent value is there for when they don't exist, say '0', to keep it simple. In fact EXST can also be done as a boolean array, an array of true/false. More like a matter of personal preference here.

Code: [Select]

ascent(i in 1..n+1) {
ascent(j in 0..lb[i]) {
let EXST[pb[i] + j] = 0;
}
}

The rest should be quite easy. Now we just make our bullet loop, and add EXST as a conditional for it;

Code: [Select]

ascent(i in 1..n+1) {
if(EXST[i] = 1) then {
CreateShotA(1, X, Y, 0);
SetShotDataA(1, 0, v, a + (i / n)(b-a), av, a, 20, RED01);
SetShotDataA(1, dur, NULL, NULL, 0, 0, 20, RED01);
FireShot(1);
}
else {

// Insert here what you would like to happen with the bullets that are in the gaps. You might want them to disappear after some time, or have a diffrent color and a diffrent (angular) velocity (also causes gaps but obstructs elsewhere too).

}


And there we have it, multiple gaps.
Now it's my turn for a question *gulp*.

Code: [Select]
ascent(i in 0..36)
{
if(f == 7860 + 60 * i)
{

CreateShotA(2, 224, 30, 0);
SetShotDataA(2, 0, 3.1415 * 210 / 52.5, 0, 180 / 52.5, 0, 20, RED03);
SetShotDataA(2, 60, NULL, NULL, 0, 0, 20, RED03);
ascent(k in 0..3) {
CreateShotA(3, 0, 0, 0);
let Rthree =  ((-GetPlayerX + GetCenterX + 210 * cos(90 - 180 / 3.5 * (k + 1)))^2 + (-GetPlayerY + GetCenterY - 210 * sin(90 - 180 / 3.5 * (k + 1)))^2)^0.5;
let angthree = 180 + atan2((-GetPlayerY + GetCenterY - 210 * sin(90 - 180 / 3.5 * (k + 1))),(-GetPlayerX + GetCenterX + 210 * cos(90 - 180 / 3.5 * (k + 1))));
SetShotDataA(3, 0, Rthree / 60, angthree, 0, 0, 20, RED02);
AddShot(15 * k + 15, 2, 3, 0);
}
FireShot(2);

CreateShotA(2, 224, 30, 180);
SetShotDataA(2, 0, 3.1415 * 210 / 52.5, 180, -180 / 52.5, 0, 20, RED03);
SetShotDataA(2, 60, NULL, NULL, 0, 0, 20, RED03);
ascent(k in 0..3) {
CreateShotA(3, 0, 0, 0);
let Rthree =  ((-GetPlayerX + GetCenterX + 210 * cos(90 + 180 / 3.5 * (k + 1)))^2 + (-GetPlayerY + GetCenterY - 210 * sin(90 + 180 / 3.5 * (k + 1)))^2)^0.5;
let angthree = 180 + atan2((-GetPlayerY + GetCenterY - 210 * sin(90 + 180 / 3.5 * (k + 1))),(-GetPlayerX + GetCenterX + 210 * cos(90 + 180 / 3.5 * (k + 1))));
SetShotDataA(3, 0, Rthree / 60, angthree, 0, 0, 20, RED02);
AddShot(15 * k + 15, 2, 3, 0);
}
FireShot(2);

ascent(k in 0..120) {
CreateShotA(1, GetCenterX, GetCenterY, 0);
SetShotDataA(1, 0, 0.6, 2 * f + 2 * k + atan((GetPlayerY - GetCenterY) / (GetPlayerX - GetCenterX)) , 0, 0, 10, BLUE11);
FireShot(1);
}
ascent(k in 0..24) {
ascent(j in 0..3) {
CreateShotA(3 + j, GetCenterX, GetCenterY, 15 * j + 15);
SetShotDataA(3 + j, 0, 0.6, 2.5 + 10 * j + 15 * k + atan2((GetPlayerY - GetCenterY),(GetPlayerX - GetCenterX)) , 0, 0, 10, RED11);
FireShot(3 + j);
}
}
}
}

Note that 'f' is my frame counter. The framework is for a recurring event every 1 second, obviously. It spawns a ring of bullets, then shoots three large bullets towards the player, in a fashion of a 4/4 music measure, (i.e. Tik-tak-tak-tak-repeat). Now that's fine and all, but I want something way more complicated than this. You see, I want to have the blue bullets, the small ones where there is a ring of 120 of, to be a ring of 180, and that they change behaviour (they're getting their velocities & accellerations reversed) when they are hit by one of the large bullets. To do that both bullet types will need to be object bullets. However it's kind of obvious I cant just make some 80000ish object bullets and use the one function that checks whether or not objects have the same location (forgot the name), since uh... that'd need to be called way too many times (79999 + 79998 + (...) + 1 = approx. 80000 * 40000 = 32000000000, way too much.). Not to mention because of the peculiar ways in which danmakufu treats arrays, how am I supposed to make the double array of objects in the first place?

Question:

a: How do I rewrite this as an object bullet function?
b: How do I then change the behavior of the blue bullets as indicated?

My thought was to again use an array like I did with solving the previous example, but a 2D-one, a matrix as it were. Each frame the matrix would be set to '0', then all positions of all object bullets would be recorded and added in the matrix. So a blue bullet would add '1' to all locations where it is, say if it'd be a square of 3x3 pixels it'd add 9 1's somewhere in the matrix. And then the same is done for the red bullets. After that we'd ascent to the size of the matrix (which isn't too large), and check for any 2's. If so we'd check if any blue bullet matches a '2'-s position and if it does remove it, and add in a new bullet, a red one with reversed directions, etc.

But uhm... how do I do that?    ::)
« Last Edit: May 11, 2009, 07:39:41 PM by Aphid »

Stuffman

  • *
  • We're having a ball!
Re: Danmakufu Q&A/Problem Thread
« Reply #19 on: May 12, 2009, 02:37:14 AM »
Quote
it probably doesnt look very good but that's not the problem. the fog does not work the way it should. It would really look nicer if it did. but the results I'm getting are bad. is it danmakufu or just a wrong thing? this is what I use:       SetFog(800, 3200, 150, 50, 50);

I think your numbers are too small and too big, respectively. Otherwise you're using it right. Try something like 1000,1500 for distances?

Infy♫

  • Demonic★Moe
  • *
Re: Danmakufu Q&A/Problem Thread
« Reply #20 on: May 12, 2009, 04:23:29 PM »
too bad, it doesn't work. the result given is similar to the second picture seen in my prevorious post.

Re: Danmakufu Q&A/Problem Thread
« Reply #21 on: May 12, 2009, 04:33:44 PM »
Did you rip the images from the game...?

Stuffman

  • *
  • We're having a ball!
Re: Danmakufu Q&A/Problem Thread
« Reply #22 on: May 12, 2009, 10:57:30 PM »
OH, now I remember. I've had this problem too, I just forgot what the solution was.

Your GraphicRects are way too huge, try to keep them under 2000 pixels or so and keep your map on a tight loop. It shouldn't need to be very big for EoSD style SDM. For some reason fog gets borked on extremely large polygons.

Drake

  • *
Re: Danmakufu Q&A/Problem Thread
« Reply #23 on: May 12, 2009, 11:04:05 PM »

A Colorful Calculating Creative and Cuddly Crafty Callipygous Clever Commander
- original art by Aiけん | ウサホリ -

Re: Danmakufu Q&A/Problem Thread
« Reply #24 on: May 13, 2009, 03:24:28 AM »
Sorry to make it look like we're ignoring you for so long Aphid, but I didn't have much time as of late to really ponder about your question. So let me get this straight, since I can't just directly copy your code to see what you're doing:

You want to spawn ~3 large object bullets aimed towards the player. You also want to spawn a massive ring of 180 (!!) bullets, that when they hit the big bullets, will change colour and reverse direction. Correct?

Do you mind explaining the massive distance ([x^2 + y^2]^0.5) and angle (atan2[y2 - y1, x2 - x1]) functions you've developed here, and are they really important to your object code? I might as well note as well: if you want anything to accelerate, your object code is going to look awfully funny, to say the least.

As far as I know, getting collision detection of objects isn't difficult, but I don't know it off the top of my head. I'll look around for you, but essentially the code is going to play out something like this for the small bullets:

"If(colliding with big bullet){
  Obj_SetSpeed(obj, -velocity);
  ObjShot_SetGraphic(obj, RED12 or whatever);
}"

If you want it to only ever change graphic and velocity once, you could impliment a counter system that counts up when colliding, so if it goes above zero you stop changing the object's properties.

Anyways, I'll think about this more after you clarify what you're looking for. I'm pretty tired at the moment, I'll admit, so my understanding of your question could be inebriated. My apologies. I am curious about your matrix system, I have to say, though it seems needlessly complex for the effect I think you're looking for.

Re: Danmakufu Q&A/Problem Thread
« Reply #25 on: May 16, 2009, 10:47:37 PM »
Me again.

This question has to do with Object Shot Bullets.

Does anyone know how to make the individual object bullets in an OBJ_SHOT script fire towards the player?

Think about the SetShotDirectionType(PLAYER) command, where 0 is equal to the player's angle. However, SetShotDirectionType(PLAYER) is completely nullified for some reason. GetAngleToPlayer does not work in my script's case, either.

Code: [Select]
#TouhouDanmakufu
#Title[Object Homing]
#Text[Test script]
#Player[FREE]
#ScriptVersion[2]

script_enemy_main {
let ImgBoss = "script\thC\IMAGE\CHAR\DOT\boss_yukari.png";

let count = 0;
let a=10;
let num=2;
let speed=3;





task Bounce(v, angle) {
     let obj=Obj_Create(OBJ_SHOT);
     let counter=0;

     Obj_SetPosition(obj, GetX, GetY);
     Obj_SetAngle(obj, angle);
     Obj_SetSpeed(obj, v);
     ObjShot_SetGraphic(obj, RED11);
     ObjShot_SetDelay(obj, 0);
     ObjShot_SetBombResist(obj, true);
     Obj_SetCollisionToObject(obj, true);


     while(Obj_BeDeleted(obj)==false){

           if(counter<=0){
           if(Obj_GetX(obj)<GetClipMinX) {

                Obj_SetAngle(obj, 0);
                Obj_SetX(obj, Obj_GetX(obj) + 0.1);
                PlaySE("script\Remilia Spell Cards\sfx\shot3.wav");
                counter++;
           }
           }

           if(counter<=0){
           if(Obj_GetX(obj)>GetClipMaxX) {

                Obj_SetAngle(obj, 0);
                Obj_SetX(obj, Obj_GetX(obj) - 0.1);
                PlaySE("script\Remilia Spell Cards\sfx\shot3.wav");
                counter++;
           }
           }

           if(counter<=0){
           if(Obj_GetY(obj)<GetClipMinY) {

                Obj_SetAngle(obj, 0) ;
                Obj_SetY(obj, Obj_GetY(obj) + 0.1);
                PlaySE("script\Remilia Spell Cards\sfx\shot3.wav");
                counter++;
           }
           }

           if(num == 56){
           DeleteEnemyShotToItem(ALL);
           }

           if(num == 60){
           ObjShot_SetGraphic(obj, RED01);
           }

       yield;
}
}







@Initialize {
SetLife(6250);
SetTimer(60);
SetScore(10000000);

SetEnemyMarker(true);

SetMovePosition02(225, 120, 60);

LoadGraphic(ImgBoss);
SetTexture(ImgBoss);
SetGraphicRect(0, 0, 128, 128);
}

@MainLoop {
SetCollisionA(GetX, GetY, 32);
SetCollisionB(GetX, GetY, 16);
yield;

if(count == 45) {
       PlaySE("script\Remilia Spell Cards\sfx\shot4.wav");
       SetMovePositionRandom01(50,20,5,GetClipMinX()+100,30,GetClipMaxX()-100,100);
loop(num){
Bounce(speed, a);

a += 360/num;
}


if(num < 60){
num += 2;
}

if(num >= 60){
num += 0;
}


if(speed > 1){
speed -= 0.05;
}

if(speed <= 1){
speed += 0;
}


count=0;
}
count++;
}

@DrawLoop {
DrawGraphic(GetX, GetY);
}

@Finalize {
DeleteGraphic(ImgBoss);
}
}

See, I want the object bullets, when they reflect off of the top and side walls, to angle towards the player (think of Kaguya's Tree-Ocean of Hourai (Last Word) and Hourai Jewel [lesser version but similar, in the main game] spell cards of IN). How do I make that happen?

Drake

  • *
Re: Danmakufu Q&A/Problem Thread
« Reply #26 on: May 16, 2009, 11:03:52 PM »
First of all, does it reflect the bullets right now to fire horizontally? If so,...

Once the bullet hits the wall, create two new variables that equal Obj_GetX(obj) and Obj_GetY(obj). Then to get the angle,

atan2(varY - GetPlayerY, varX - GetPlayerX)

This arctan function will get the tangential angle based on the horizontal and vertical distance separating the location and the player. It's just a more complicated GetAngleToPlayer. Although I'm not sure why it wouldn't be working...
« Last Edit: May 16, 2009, 11:05:44 PM by Drake »

A Colorful Calculating Creative and Cuddly Crafty Callipygous Clever Commander
- original art by Aiけん | ウサホリ -

Re: Danmakufu Q&A/Problem Thread
« Reply #27 on: May 16, 2009, 11:12:05 PM »
atan2(GetPlayerY - Obj_GetY(obj),GetPlayerX - Obj_GetX(obj));

The arctangent of y/x (atan2) will get the angle from any point to any point. The formula is:
atan2(DestinationY - OriginY, DestinationX - OriginX);

To Drake, you don't need to set them as variables. Getting the angle the moment the bullet hits the wall should be fine.

So:

Code: [Select]
if(Obj_GetY<GetClipMinY){
  Obj_SetAngle(obj, atan2(GetPlayerY - Obj_GetY(obj), GetPlayerX - Obj_GetX(obj));
  PlaySE("script\Remilia Spell Cards\sfx\shot3.wav");
  counter++;
}

Etc.

Re: Danmakufu Q&A/Problem Thread
« Reply #28 on: May 16, 2009, 11:47:31 PM »
Huh. I guess I used the atan function wrong (I did try it, but I put it in the wrong area).

Thank you.


Also, one other thing: How can I shoot a ring of bullets that are set in a random position while still maintaining the ring (Flandre's last spell card)?
« Last Edit: May 17, 2009, 12:00:36 AM by Frazer »

Re: Danmakufu Q&A/Problem Thread
« Reply #29 on: May 17, 2009, 12:25:17 AM »
Set your random origin as a variable that you can call upon in your CreateShot function in the same frame. Like this:

Code: [Select]
if(frame==120){
 let x = rand(0, 448);
 let y = rand(0, 480);
 loop(60){
  CreateShot01(x, y, 3, angle, BLUE12, 10);
  angle+=360/60;
 }
 frame = 0;
}

This way you can call upon the random origin you've created for the bullet ring.