~Hakurei Shrine~ > Rika and Nitori's Garage Experiments

Another Danmakufu Tutorial?

Pages: << < (2/15) > >>

Drake:

The important parts of the Improvement thread.

+++++

Iv'e recently been working on functions and scripts to improve danamkufu but it hasnt been working like ive been wanting it, so il need help...Here's a few things on the agenda so far:

Better Laser Sources:

Danmakufu has a terrible way of making laser sources, Their too big on thin lasers and too small on big lasers (They turn CtC's beam graphic into a giant block). These sources also end up forming a giant blob when topped up on top of each other. What im thinking of is a script (Either enemy or shot, whichever does the job) that could:

-Be resized
-Choose color
-Have a delay and activated size to match the laser's
-Can -snap- onto a specific lasers for lasers that move
-Can flicker on smaller lasers
-Whatever would be convinient


Up-to-date Cutins

Dont you just envy the new cutin scripts on the newer touhou games? What im thinking for this one would be a drawing function in the boss that would activate at the start of regular spellcards, You would ignore the actual Cutin script. This drawing function would also be easely editable for those who want to expiriment with their own custom-cutin functions.


Templates for the default graphics and sounds

Alot of the graphics and sounds in danmakufu are pretty bland or just poorly drawn, and dont even get me started on the sound of ripping paper every time you kill an enemy. Luckely these can be changed from what CtC thought us, Im thinking of a few folders that could show you what the filenames of the Danmakufu graphics are and what to replace them with.


Cut-trough lasers

The hardest of my ideas so far, in the newer games when you bomb trough a laser it cuts in several pieces and every piece is properly resized, Im thinking of somehow making object lasers (or script if necesary) that would check for bombs, and resize into several different lasers based on which parts were touched...I think this one might actualy be imposible.

Anyways il wait to see what you guys have to suggest or even help me out on this, My msn is Samuel91119@hotmail.com

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

#include won't work for drawing the cutins, I don't think.

Instead of calling a cutin, create a boolean upon loading. When a cutin is needed, change it to true. An 'if' statement inside the drawing loop activates. It should call a separate function that displays the graphic, moves it, resizes and all that junk based on what arguments the user calls.

Should be easy to do, in theory.

As for resizing lasers, only way to do that without ugliness is having a preloaded image with the laser graphics that you want at different widths. Lengths don't change a straight graphic much, so they should be fine as long as you don't have a stump laser. Yet again, the process would be the same as above, but they'd be objects instead. This means that you could still rotate and move it around easily. Very little resizing; the size argument indicates which image you use, and little discrepancies would be resized, and at certain widths the graphic would just change to a different one. If vertices could be used, they might help for a 3D effect as well.

With this, you could probably do the split-laser thing as well (should be easy enough to create a shitload of small copies of the original), but controlling every little laser would probably be hell.

Sounds that you can change by filename are

sePlayerShot01.wav   
seGraze.wav   
sePlayerCollision.wav (?)
sePlayerSpellCard.wav (?)
se1UP.wav   
seScore.wav   
seBomb_ReimuA.wav   
seExplode01.wav   
seBomb_ReimuB.wav
seUseSpellCard.wav (?)
seGetSpellCardBonus.wav (?)
seEnemyExplode01.wav   
seSuperNaturalBorder1.wav   
seSuperNaturalBorder2.wav

and

STG_Player01.png
STG_Player11.png
CutIn_PlayerChar01.png
CutIn_PlayerChar11.png
Select_Player01.png
Select_Player11.png
STG_Frame.png

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

Has anyone checked to see how a laser graphic looks when you scale it down, rather than up? If it looks fine scaling down you could just use a high-definition shot graphic (like greater than 100 pixels) and stretch it however you want, and it wouldn't look nearly as bad as a tiny bullet blown up to Master Spark size.

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

You'd think so. Most things are actually still possible, there just isn't enough direct code to simplistically get what you want accomplished. Requires an intense amount of dicking around. But I do agree, I'm not seeing how split lasers could be done. Active collision detection of a laser, then create two lasers on either side of the collision point... Or more? Sounds iffy to me. Can you even call upon non-circular collision detection? How does collision detetction of lasers even work (dozens of small circles, or just an elongated circle/oval)?

What Koishi did was create the laser source (the delay cloud thing) and the laser seperate of eachother, so instead of that really shitty spread thing you see when you make a wide laser, it looks something like this:
http://i641.photobucket.com/albums/uu134/Nautth/CtC-Stylebiglasers.png

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

http://mp3splt.sourceforge.net/mp3splt_page/home.php

LoadMusic(track);
PlayMusic(track);

let min = 0;
let sec = 0;
let cen = 0;
    //current time, starts at 0'0"0
let loopmin = 3;
let loopsec = 42;
let loopcen = 58;
    //time to loop at

@MainLoop{

    cen++;    //every frame, cen will increase
    if (frame % 60 == 0){sec++;}    //every 60 frames, sec will increase
    if (frame == 3600){min++; frame = 0;}    //every 3600 frames (or 60 seconds), min will increase

    if (cen == loopcen && sec == loopsec && min == loopmin){    //if you're at the loop time
        LoadMusic(looptrack);
        PlayMusic(looptrack); //play the split track
        loopmin = 1;
        loopsec = 23;
        loopcen = 54;
        min = 0;
        sec = 0;
        cen = 0;
            //reset the current time and change the time to loop at
    }

}

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

A good idea, Drake.  One suggestion, though:  It may be better to use the GetTime() function instead of counting frames.  If the game starts experiencing slowdown, your counter will start lagging behind, resulting in a late restart of the loop (it could happen the other way, too, if the game starts playing too fast for some reason).

This function gives you the time, in milliseconds, that the script has been running.  All you need to do is keep track of when you started the current loop, and check for when it should be restarted using this simple formula:
time_to_restart_at = time_loop_started + (minutes * 60000) + (seconds * 1000) + milliseconds

The just do a simple check to see if you should restart the loop:
if (GetTime() > time_to_restart_at) <restart the loop>;

This still won't be perfect, since it only gets called once per frame and has the possibility of not lining up perfectly with the music, but if you set it up right it should be good.



I've been making extensive use of this type of stuff in a new, secret project which I'll never finish cause I'm a lazy dumbassI've been a bit too busy to work on effectively.  Hopefully I'll find motivation to finish it up soon!


I should mention, though - I highly recommend against using GetTime() for anything related to the actual gameplay normally, since that will very likely break replays (since the timing could very well be different each time it is run).

Hat:

GetTime() could be used for looping, though, right? It wouldn't affect gameplay at all.

And what of character scripts? I could help with something like that... but to most vet programmers, here, they're really easy. >_> Not to me they aren't. XD

Naut:

Just go ahead and dump your shit here then Drake... Haha!

Nuclear Cheese:


--- Quote from: Karfloozly on April 24, 2009, 03:59:08 AM ---GetTime() could be used for looping, though, right? It wouldn't affect gameplay at all.

--- End quote ---

GetTime() is perfectly safe to use on anything that does not, in any way, affect how the game plays out.  Looping music (Drake's idea, with my suggestion of using GetTime() ) is a perfect example - it doesn't change anything that happens in the game.  Another example that would be okay is animating a character based on realtime (that is - only their sprite animation, NOT their movement/attack pattern/hitbox/etc).

There are (as I posted in the old forums) two main reasons why you shouldn't use GetTime() for any gameplay-related items.  First, it is practically guarenteed to break replays if something is based on real time instead of the framerate, since even a slight difference in playback speed when watching a replay could completely throw off the replay.  Second, depending on what, exactly, is dependant on GetTime(), significant slowdown (or speedup) could cause a normally balanced pattern to sway towards either rediculously easy or rediculously hard or even impossible.

If these are not a concern for your script, then you can go ahead and use GetTime() in ways that affect gameplay.  If you do, it would be nice to at least inform the player (in a readme or whatnot) that these anomalies might occur.  I highly recommend against it for serious scripts, though.


Naut - an intermediate tutorial would be a good thing to add to our collection.  I'd be glad to help as I can.  No MSN account, but you can post here or PM me and I should be able to reply within a reasonable amount of time (at least when work isn't sapping me dry of willpower :-\).

Naut:

Here's the first section. Opinions?


Intermediate Bullet Patterns

Since you've read Blargel's Basic tutorial, you should be familiar with all the CreateShot functions, as well as loop(){}. The latter you will likely be using often, as you can spawn a myriad of patterns by repeating a function while augmenting a value. Don't worry -- I'll show you what I mean. Note that anything after and on the same line as "//" in Danmakufu scripts is a comment, and will not be read by Danmakufu. Be sure to comment your work often so you don't forget what you're doing.


--- Code: ---#TouhouDanmakufu
#Title[Spawning Bullets in a Circle]
#Text[Using loop to spawn bullets in a circular pattern.]
#Player[FREE]
#ScriptVersion[2]

script_enemy_main{


let imgExRumia="script\ExRumia\img\ExRumia.png";
let frame = 0;
let angle = 0;


@Initialize{
SetLife(1500);
SetTimer(60);
SetInvincibility(30);
LoadGraphic(imgExRumia); //Load the boss graphic.
SetMovePosition02(GetCenterX, GetCenterY - 100, 120); //Move the boss to the top center of the screen.
}

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

if(frame==120){

loop(36){
CreateShot01(GetX, GetY, 3, angle, BLUE12, 0);
angle += 360/36;
}

angle = 0;
frame = 60;
}


}

@DrawLoop{
//All this foolishness pertains to drawing the boss. Ignore everything in @Drawloop unless you have read and understand Nuclear Cheese's Drawing Tutorial.
SetColor(255,255,255);
SetRenderState(ALPHA);
SetTexture(imgExRumia);
SetGraphicRect(64,1,127,64);
DrawGraphic(GetX,GetY);
}

@Finalize
{
DeleteGraphic(imgExRumia);
}
}
--- End code ---

Begin by creating a text file with the former code in it, and run it. The main thing you should be looking at is in @Mainloop: the if(){} statement. You'll notice we loop CreateShot01 and angle. What loop does is it repeats everything inside it's braces however many times you've defined it to, in the same frame. In this case, we told Danmakufu to repeat everything inside loop 36 times, which means it will spawn 36 bullets on the boss' position, at an angle of angle, in the same frame. At the start of the script, we defined angle as 0, so all the bullets should fire at 0 degrees, or to the right. But if you run this script, you'll see that they form a perfect circle. Why? Well, that's because we set angle to be angle += 360/36 everytime the loop is run, so angle will hold a different value (in this case, 10 more than it used to) everytime we create a bullet. What this does is creates a perfect circle, because every bullet created will fire at ten degrees more than the last one, which will total to be 360 degrees because the loop is run 36 times (36x10=360).

The reason I put angle += 360/36; instead of angle += 10; was to show you that for any number that you loop(num), the angle you'll want to increment by in your loop will be angle += 360/num;. So if you want 24 bullets in your circle, the code would be:


--- Code: ---if(frame==120){
loop(24){
CreateShot01(GetX, GetY, 3, angle, BLUE12, 0);
angle+=360/24;
}
angle = 0;
frame = 60;
}
--- End code ---

We reset the value of angle after the loop to make sure it still holds a value that we can work with later in the script, but we don't really need to do this. Sometimes purposefully dividing by a different value than what was looped can create an interesting pattern, and you might not want angle to be reset afterwards.

A simple way to change this code would be to have the angular (4th) parameter of CreateShot01 be angle + GetAngleToPlayer, which would force one particle to always be shot towards the player.

Alright, you now know how to spawn bullets in a circle, and how to increment values in loop structures to create circular patterns. But what if you want the bullets to be spawned some distance away from the boss, but still in a circle? For that, we'll use the trigonometric functions sin and cos.

The sin of an angle is the y-coordinate of a point along a circle with a radius of one.



The cos of an angle is the x-coordinate of a point along a circle with a radius of one.



Note the reversed y-coordinates, remember that Danmakufu has the positive y direction pointing downward.

So, if we want something to spawn at the point (cos60, sin60), where the boss is the origin of the circle, we say:


--- Code: ---CreateShot01(GetX + cos(60), GetY + sin(60), 3, 60, BLUE12, 10);

--- End code ---

But this code won't really do anything, because we're only spawning it a distance (radius) of 1 pixel away from the boss, which we won't see. So to spawn it with a distance of say, 100 pixels away from the boss, the code would be:


--- Code: ---CreateShot01(GetX + 100*cos(60), GetY + 100*sin(60), 60, BLUE12, 10);

--- End code ---

Now we'll see some effects. A distance of 100 pixels away from the boss is pretty noticable. Just multiply sin and cos by a value, which will be your radius, to spawn particles that distance away from your origin, which in this case is the boss. So, to combine and summarize everything so far, I give you the following to place in the beginning script:


--- Code: ---if(frame==120){
loop(36){
CreateShot01(GetX + 60*cos(angle), GetY + 60*sin(angle), 3, angle, BLUE12, 12);
angle += 360/36;
}
angle += 4;
frame = 112;
}
--- End code ---

This code will continuously spawn bullets in a circle around the boss with a radius of 60 pixels. I set angle to be angle += 4; outside the loop to make the circle shift it's position 4 degrees everytime we spawn it, so it looks like the circle is spinning.

The format for spawning bullets in a uniform circle is:
[x, y] -> [originX + radius*cos(angle), originY + radius*sin(angle)]

Keep in mind that you don't just have to increment angle in a loop structure. You can increment any other variable, like radius, as well. Incrementing radius will make the circle look like it is expanding or contracting, or have other interesting effects if included inside the actual loop{}.



Using only the trigonometric functions, as well as our knowledge of loop structures and incrementing values, we can make some pretty nice patterns. Here's an example script using everything we've learned so far. Can you tell what's happening?


--- Code: ---#TouhouDanmakufu
#Title[Border of Wave and Tutorial]
#Text[How to use incrementing values and loop structures to create complex danmaku.]
#Player[FREE]
#ScriptVersion[2]

script_enemy_main{


let imgExRumia="script\ExRumia\img\ExRumia.png";
let frame = 0;
let frame2 = 0;
let angle = 0;
let angleAcc = 0;
let radius = 0;


@Initialize{
SetLife(4000);
SetTimer(60);
SetInvincibility(30);
LoadGraphic(imgExRumia);
SetMovePosition02(GetCenterX, GetCenterY - 120, 120);
}

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

if(frame==120){

loop(6){
CreateShot01(GetX + radius*cos(angle), GetY + radius*sin(angle), 3, angle, BLUE12, 12);
angle += 360/6;
}
angle += angleAcc;
angleAcc += 0.1;
frame = 119;
}

if(frame2>=-140 && frame2 <=110){
radius++;
}
if(frame2>=111 && frame2 <= 360){
radius--;
}
if(frame2==360){
frame2=-141;
}

}

@DrawLoop{
SetColor(255,255,255);
SetRenderState(ALPHA);
SetTexture(imgExRumia);
SetGraphicRect(64,1,127,64);
DrawGraphic(GetX,GetY);
}

@Finalize
{
DeleteGraphic(imgExRumia);
}
}
--- End code ---

In this code, we have bullets spawn at six points around a uniform circle with a radius radius, which is being controlled by a second frame counter frame2. If frame2 is between -140 and 110, then the radius will increase, if it is between 111 and 360, the radius will decrease. The angle these bullets is being fired off at is always being increased by angleAcc, which is also increasing, so the circle will spin at an increasingly faster rate. Combining the oscillation of radius and the ever increasing rate of angle creates a pretty complex pattern, don'tcha think? Feel free to experiment by changing values, like changing angleAcc+=0.2 or radius+=0.5.


Pages: << < (2/15) > >>

Go to full version