Maidens of the Kaleidoscope

~Hakurei Shrine~ => Rika and Nitori's Garage Experiments => Topic started by: Naut on April 23, 2009, 11:50:00 PM

Title: Another Danmakufu Tutorial?
Post by: Naut on April 23, 2009, 11:50:00 PM
Since the archived forums aren't going to be up forever, all of our delicious reference scripts are being flushed down the toilet. So this leaves remarkably little information regarding how to script in Danmakufu, aside from the random script you may have saved from the old forums, and the three tutorials (two of which don't explain how to actually code attacks, just dress them up).

So, I've begun work on an "intermediate" Danmakufu tutorial, which basically picks up where Blargel's tutorial left off. It will include:
-Mediocre techniques on how to produce semi-complex patterns (looping bullet creation to make rings of bullets, incrementing variables in loops to produce patterns, spawning bullets in uniform circles around an origin using sin and cos, etc).
-Explanation of tasks and how to use them effectively
-Boss movement (because the basic tutorial did not include a boss movement section)
-How to use object bullet creation and tasks to make scripted indestructable bullets
-How to string scripts together to make a mini-stage/plural script

So this is all I can think of. Anything that I should add to the list, or perhaps take off? I already have the first section completed (mediocre bullet patterns), so if you'd like to personally help with the tutorial or see it in it's current state for whatever reason, my MSN is Nautth@hotmail.com
I welcome any and all assistance.
Title: Re: Another Danmakufu Tutorial?
Post by: Stuffman on April 23, 2009, 11:59:06 PM
According to 7HS we'll find out this weekend if the DMF wiki is going to survive, these would be worthy additions to it.
Title: Re: Another Danmakufu Tutorial?
Post by: Naut on April 24, 2009, 12:12:14 AM
What's going on this weekend? Also, nice Mod position.
Title: Re: Another Danmakufu Tutorial?
Post by: Stuffman on April 24, 2009, 12:53:58 AM
Oh I asked 7HS about the wiki in IRC and he said he was going to fool around with it this weekend, that's all. So we'll find out if the migration will be any trouble or not.

Also uh yeah apparently they needed someone to mod for this board and they wanted someone with Danmakufu knowledge so I'm a mod now. I doubt I'll need to do much actual moderation, but if we need to shuffle stickies or whatever as we improve our resources, I've got the tools to do it. I suppose this weekend I'll go digging through the old boards and see if there's anything else useful I care to transfer.

My green name is more exciting than the actual powers.
Title: Re: Another Danmakufu Tutorial?
Post by: Naut on April 24, 2009, 01:06:42 AM
"Fool around with it..."

Sounds enthralling.

The green name is pretty sexy, gotta say. But it doesn't appear green in the "online users" bar. You got ripped off, methinks.
Title: Re: Another Danmakufu Tutorial?
Post by: Drake on April 24, 2009, 02:17:49 AM
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).
Title: Re: Another Danmakufu Tutorial?
Post by: Hat on April 24, 2009, 03:59:08 AM
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
Title: Re: Another Danmakufu Tutorial?
Post by: Naut on April 24, 2009, 04:55:43 AM
Just go ahead and dump your shit here then Drake... Haha!
Title: Re: Another Danmakufu Tutorial?
Post by: Nuclear Cheese on April 24, 2009, 11:48:47 PM
GetTime() could be used for looping, though, right? It wouldn't affect gameplay at all.

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 :-\).
Title: Re: Another Danmakufu Tutorial?
Post by: Naut on April 25, 2009, 12:24:58 AM
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: [Select]
#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);
}
}

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: [Select]
if(frame==120){
loop(24){
CreateShot01(GetX, GetY, 3, angle, BLUE12, 0);
angle+=360/24;
}
angle = 0;
frame = 60;
}

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.

(http://i641.photobucket.com/albums/uu134/Nautth/sin.jpg)

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

(http://i641.photobucket.com/albums/uu134/Nautth/cos.jpg)

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: [Select]
CreateShot01(GetX + cos(60), GetY + sin(60), 3, 60, BLUE12, 10);

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: [Select]
CreateShot01(GetX + 100*cos(60), GetY + 100*sin(60), 60, BLUE12, 10);

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: [Select]
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;
}

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: [Select]
#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);
}
}

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.
Title: Re: Another Danmakufu Tutorial?
Post by: Stuffman on April 25, 2009, 12:38:11 AM
Oh cool, you even explained sin/cos in a clear way.

Not really a significant gripe but the angle shown in your image would actually be 300 or -60 degrees in Danmakufu, that might confuse some people.
Title: Re: Another Danmakufu Tutorial?
Post by: Naut on April 25, 2009, 12:39:36 AM
Not really a significant gripe but the angle shown in your image would actually be 300 or -60 degrees in Danmakufu, that might confuse some people.

I actually just noticed that. Gonna redo the images to compensate for Danmakufu's needlessly different coordinate grid.

Aaaand fixed.
Title: Re: Another Danmakufu Tutorial?
Post by: Hat on April 25, 2009, 01:04:39 AM
Yeah, I would very much like to at least know the way the danmakufu coordinate grid works. I hate having to type up some polar absolute pattern, and then it shoots, like... ninety degrees to where I wanted it to go. >_>

I'll take the character script thing as a no, then.
Title: Re: Another Danmakufu Tutorial?
Post by: Stuffman on April 25, 2009, 01:21:47 AM
The grid works pretty simple actually, starts at 0,0 in the upper left corner. Increase x to go right, increase y to go down.

Degrees start at 0, pointing to the right, going clockwise. So hard right is 0, down is 90, left is 180, up is 270.

And I was actually thinking I would try to tackle a tutorial on how to make a player script.
Title: Re: Another Danmakufu Tutorial?
Post by: Naut on April 25, 2009, 01:31:04 AM
You're free to create character scripts if you want, and we'll help in any way we can.

The Danmakufu coordinate grid is the exact same as a cartesian plane (http://en.wikipedia.org/wiki/Cartesian_plane), except the positive y direction points downwards. So the point (0, 0) is the top left of the gaming window, (448, 0) is the top right, (0, 480) is the bottom left and (448, 480) is the bottom right.

Note that your hitbox can only be +-32 pixels inside of this rectangle for the x directions and +-16 pixels for the y directions, so you can never actually touch the sides of the playing field.

(http://i641.photobucket.com/albums/uu134/Nautth/DanmakufuWindow.jpg)

And damn, Stuffman beat me to the explanation. Oh well.

And I was actually thinking I would try to tackle a tutorial on how to make a player script.

Please do this. It would be nice to know how to create proper player scripts.
Title: Re: Another Danmakufu Tutorial?
Post by: Stuffman on April 25, 2009, 01:34:30 AM
Well diagrams are nice too!
Title: Re: Another Danmakufu Tutorial?
Post by: Naut on April 25, 2009, 01:38:18 AM
Hells yeah diagrams!
Title: Re: Another Danmakufu Tutorial?
Post by: Hat on April 25, 2009, 01:38:37 AM
Well, I meant a tutorial, aheheh... well, I'll finish making a working character script, then we'll see. Yuka is still in progress. Character scripts are just very tough to describe. It's like trying to describe a level script, but from scratch.
Title: Re: Another Danmakufu Tutorial?
Post by: Naut on April 25, 2009, 01:48:33 AM
Our explanations aren't sufficient!? Well, tell us what don't you understand specifically, and we'll see what we can do! Some equation you're using not working properly? Can't get used to the clockwise degree system? Something else!? Ask us, and we'll explain!
Title: Re: Another Danmakufu Tutorial?
Post by: Stuffman on April 25, 2009, 01:58:00 AM
Yeah dude, that's what we're getting paid for.

(We're getting paid, right?)
Title: Re: Another Danmakufu Tutorial?
Post by: Naut on April 25, 2009, 05:22:22 AM
Well, if any of us is it's you, Mr. Green-name.
Title: Re: Another Danmakufu Tutorial?
Post by: Iryan on April 25, 2009, 04:59:46 PM
I'll just try to contribute by saying something on tasks and object bullets.

With the CreateShotA command, you can already program rather complex bullet movements. However, what if you want to create indestructable bullets? What if you want your bullet to react to stuff that happens after you fired it, for example, to bounce off of the border of the playing field? That is when you will need object bullets.


To program an object bullet, you have to create a task.


Tasks are basically smaller scripts that run alongside the regular MainLoop. The task script is usually inserted after the @Finalize loop before the closing bracket of script_enemy_main and can later be called like a regular function. To script a task, the following code is used:
Code: [Select]
task NameOfTheTask(put, variables, here) {
        stuff happens;
        }
So far, it looks much like a script for a regular function, and it pretty much is. To really make it run besides your main loop, you have to include a while statement. That way, the task works as long as this condition is met. Example:
Code: [Select]
task Testtask(a, b, c) {
         let timer=60;
         while(timer>0) {
               stuff happens;
               timer--;
              }
        }
For a duration of 60 frames, every frame stuff will happen. After that, the "while" condition is no longer met, and the task expires.

But, because tasks run separate from the MainLoop, there is a very important command:
Code: [Select]
yield;The yield command says the program to stop the task it is working on and to look if there is other code to perform. This includes the MainLoop. That means you have to insert the yield; command at the end of the MainLoop and inside the recurring part of your task. Otherwise, danmakufu will proceed to ignore everything else and keep focused on the MainLoop (or the task).
That means the above code is actually wrong as it will do the "stuff" 60 times, but all during the same frame. The corrected version of that code would be:
Code: [Select]
task Testtask(a, b, c) {
         let timer=60;
         while(timer>0) {
               stuff happens;
               timer--;
               yield;
              }
        }

The yield command can also be called repeatedly to suspend the task. For each yield inserted, danmakufu will run the MainLoop once before the stuff after the yields proceeds. This means that by inserting the code:
Code: [Select]
      loop(n){ yield; } into the task, the following code will suspend for n frames.

Tasks performed by danmakufu are independant from each other.
This means that you can call the task multiple times while it is still running. These don't affect each other and are performed side by side. It also means that, like with regular loops, variables and arrays created in a task don't exist outside the task.



So, now that you know something about tasks, how does one make object bullets?


First, you have to assign an ID for your bullet. As the ID is assigned inside the task and thus doesn't exist outside of it, you can practically use anything. The most common way is to call it "obj". You assign the ID like this:
Code: [Select]
      let obj=Obj_Create(OBJ_SHOT);This creates an object that is classified as an object bullet and can be referred to by the name "obj". It has, however, no properties as of now - they all have to be assigned with a separate command. The most important are:
Code: [Select]
    Obj_SetPosition(obj, x coordinate, y coordinate);
    Obj_SetAngle(obj, angle);
    Obj_SetSpeed(obj, speed);
    ObjShot_SetGraphic(obj, graphic);
    ObjShot_SetDelay  (obj, delay);
    ObjShot_SetBombResist (obj, true or false);
In order, they set the position, angle, speed, bullet graphic, delay before changing from vapor to damaging bullets, and whether the bullet is immune to bullets and death explosions (true) or can be bombed like a regular bullet (false).
These commands should be called right after the declaration of your object bullet. The can, however, come in handy to regularly change the property of your bullet. For example, there is no simple command to give the bullet a specified angular velocity. To give it one, you have to call Obj_SetAngle every loop.

Speaking of which, how do you define your looping action for a bullet?
Aside from some advanced shenanigans, the looping part should be called every frame, as long as the bullet still exists, right? In danmakufu terms, this is expressed as follows:
Code: [Select]
        while(Obj_BeDeleted(obj)==false) {
               stuff happens;
               yield;
        }
The stuff happens once every frame, as long as the statement "the object has been deleted" is a false statement. Or, to put it easier, it happens once every frame as long as the bullet still exists.

So now we have the basic structure to create an object bullet:
Code: [Select]
task Bullet(x, y, v, angle) {
      let obj=Obj_Create(OBJ_SHOT);

      Obj_SetPosition(obj, x, y);
      Obj_SetAngle(obj, angle);
      Obj_SetSpeed(obj, v);
      ObjShot_SetGraphic(obj, RED01);
      ObjShot_SetDelay  (obj, 0);
      ObjShot_SetBombResist (obj, true);

      while(Obj_BeDeleted(obj)==false) {
              yield;
       }
Put this behind "@Finalize", and you can use "Bullet(x, y, v, angle);" just like a regular function to create a normal indestructable bullet.


Now, let's say we want the bullet to react to something. For the bullt to react to something, we must be able to check whether or not the conditions of reaction are given. For this, we have the information gathering commands, such as:
Code: [Select]
Obj_GetX(obj);
Obj_GetY(obj);
Obj_GetAngle(obj);
Obj_GetSpeed(obj);
I'm pretty sure you can guess what these do.  :P

Now, let's say that we want a bullet that reflects from the left and right border of the playing field. To do this, we specify in the "while" part that, if the bullet is too far on the left or right, it's angle shall be adjusted like with a real light reflection - incoming angle equals leaving angle. Because of how angles are coded, this requires some trickery:


Code: [Select]
task Bullet(x, y, v, angle) {
      let obj=Obj_Create(OBJ_SHOT);

      Obj_SetPosition(obj, x, y);
      Obj_SetAngle(obj, angle);
      Obj_SetSpeed(obj, v);
      ObjShot_SetGraphic(obj, RED01);
      ObjShot_SetDelay  (obj, 0);
      ObjShot_SetBombResist (obj, true);

      while(Obj_BeDeleted(obj)==false) {

              if(Obj_GetX(obj)<GetClipMinX) {
                     Obj_SetAngle(obj, 180 - Obj_GetAngle(obj) );
                     Obj_SetX(obj,  Obj_GetX(obj) + 0.1);
              }

              if(Obj_GetX(obj)>GetClipMaxX) {
                     Obj_SetAngle(obj, 180 - Obj_GetAngle(obj) );
                     Obj_SetX(obj,  Obj_GetX(obj) - 0.1);
              }

              yield;
       }
This code creates an indestructable bullet that checks each frame wheter it is right or left from the actual playing field. If that is the case, it will adjust the angle to mimic a real reflection, then move the bullet a little towards the center of the field to prevent it from rapid bouncing along the border.


             /\ 270?
<---- 180?           0? --->
             \/ 90?

I just noticed that I lack the proper english vocabulary to describe why the reflected angle is calculated as it is.  :-\
It would be nice if someone else could elaborate. You know, someone who has a great deal of knowledge about angles and danmakufu and who also likes to explain stuff to others.


Nevertheless, now you can fire your very own indestructable-bouncing-of-the-sides-bullets, just with a little "Bullet(x, y, v, angle);"!
Just don't forget to insert a yield command in your MainLoop, or the bullet won't bounce!
Title: Re: Another Danmakufu Tutorial?
Post by: Hat on April 25, 2009, 06:27:58 PM
Yeah, the rapidbounce things are funny... XD When I first tried to make a bouncing bullet, I caked the sides of the screen with these little vibrating balls. I couldn't stop laughing.

D=< We really need our own Danmakufu forum again.
Title: Re: Another Danmakufu Tutorial?
Post by: Naut on April 25, 2009, 07:22:39 PM
To really make it run besides you main loop, you have to include a while statement. That way, the task works as long as this condition is met. Example:
Code: [Select]
task Testtask(a, b, c) {
         let timer=60;
         while(timer>0) {
               stuff happens;
               timer--;
              }
        }
For a duration of 60 frames, every frame stuff will happen. After that, the "while" condition is no longer met, and the task expires.

I'm pretty sure this function will not take 60 frames, but will infact take one frame to complete, because while is repeatedly checked in the same frame for it's conditions. So really, this snippit of code will take one frame to execute, but whatever is inside of the while statement will happen 60 times.
The reason why in tasks that it would take 60 frames to complete is because when you have yield; inside the while loop, the task is suspended from continuing to check if the condition is true that frame, but the next frame it gets it's chance again.

Unless my understanding of while is incorrect?

Also! Your mirror bullet code could be better expressed with a counter:
Code: [Select]
task ReflectBullet(x, y, v, angle, graphic, delay){
let obj = Obj_Create(OBJ_SHOT);
let counter = 0;
Obj_SetPosition(obj, x, y);
Obj_SetAngle(obj, angle);
ObjShot_SetGraphic(obj, graphic);
ObjShot_SetDelay(obj, delay);
ObjShot_SetBombResist(obj, true);
Obj_SetSpeed(obj, v);

while(! Obj_BeDeleted(obj)){
if(counter==0){
if(Obj_GetX(obj)<=GetClipMinX){
Obj_SetAngle(obj, 180 - angle);
counter++;
}
}
if(counter==0){
if(Obj_GetX(obj)>=GetClipMaxX){
Obj_SetAngle(obj, 180 - angle);
counter++;
}
}
yield;
}
}

This way the bullet will check a counter and reflect only once because the counter increases once it's reflected, instead of vibrating on the sides of the walls like your current code.

May I include your essay in the intermediate tutorial?
Title: Re: Another Danmakufu Tutorial?
Post by: Iryan on April 25, 2009, 07:59:14 PM
You are correct. It will probably loop only once. I am aware of this. That's why I say so directly afterwards when I explain the yield command.  ;D I didn't state it directly, though, and probably should've included something like "so, the corrected version of the above would be (insert same code with yield; in the while{} brackets).

On the second part, well...
Your edit has the disadvantage of the bullet bouncing only once in total, while my version will bounce any number of times until it hits the upper or lower border of the playing field.
If you fire such bullets at an angle almost equal to 0 or 180, you may, on the long term, have a wall of bullets amassing if you call the event many times. The same goes for bullets that bounce from the top and the bottom. To prevent this, one might add a counter and include an additional condition like (&&counter<3).
I used bouncing bullets in a script where a definite number of three bouncing bullets would stay in an inclosing laser cage while regularly spawning bullet rings. This problem couldn't arise. However, the bullets where trapped inside a single laser and only freed once the laser moved past the bullet, leaving it outside the cage. My version is there to circumvent this rapid bouncing that traps the bullet inside the mirror axis.

And if you want to add it to the tutorial, I'd be glad, though I think there are some typing errors that I have to fix. Also, I wanted to describe how to replicate Aya-Satori's final spellcard using object bullets, so it would be better to wait for a bit. Should happen in ~ 3 weeks at the latest.  8)
Title: Re: Another Danmakufu Tutorial?
Post by: Naut on April 25, 2009, 08:25:06 PM
Fair enough about the first part.

In regards to the mirror bullet code, the big problem is that if you set a rather high speed value, the position of the bullet can go long past GetClipMinX and GetClipMaxX, so setting it's position to +-0.1 pixels only corrects it for slow speeds. Extremely slow speeds as a matter fo fact... Like, less than or equal to 1. My code, though having the limitation of only being reflected once, can assume any realistically dodgable speed value and still work. Setting the if(counter<=num) to a higher value doesn't work unfortunately, for the same reason your +-0.1 pixel fix doesn't work; that is, the bullet can stay beyond GetClipMinX and GetClipMaxX for more than one frame if a high speed value is called. Perhaps osmosis of our two codes could solve both problems, but I don't really want to think about it right now.

Aside from a few spelling and grammatical errors, your explanation of tasks is quite nice. Things I would generally want to include would be:
-Why we say while(! Obj_BeDeleted(obj)){}
(While the object is currently in existance, do this... Or something to that effect)

-A brief explanation of function versus task
Why we use one over the other, limitations of functions, etc.

-A way to use tasks to accomplish other simple things, like ordered events.
For example, in a stage script, you use a long string of yield; commands and events to dictate how a stage would play out (if there are still enemies on the screen, yield;, etc.). A breif explanation on how to do this might flow well with this tutorial (not specifially how to code stage scripts, but other, simpler things).
Title: Re: Another Danmakufu Tutorial?
Post by: Iryan on April 25, 2009, 09:02:58 PM
As long as you have a real reflection, the incoming speed shouldn't matter because, on a left/right reflection, the horizontal speed will be the same afterwards, only negative. The only problem that should be possible to arise is when the reflecting border is moving, in which case the bullet should be moved by at least the same amount of pixels the border moves per frame.

Use the task explanation as you want.  ;D
Your additions seem appropriate, though I don't exactly know the (dis-)advantages of tasks besides "tasks can make suspended and looping stuff" and "tasks take more time to type and may or may not go harder on the PC".
Also, I have no idea at all how stage scriptss work. All I've done are singles and plurals.
Title: Re: Another Danmakufu Tutorial?
Post by: Hat on April 25, 2009, 10:52:56 PM
Okay, really now? The easiest way to circumvent the vibrating phenomenon would just be to include a timer. Suppose you had something like
Code: [Select]
while((GetX == GetClipMinX || GetX == GetClipMaxX) && timer <= 0){
  Insert All Your Reflection Junk Here
  timer = 10; //for example
  yield;
}
if(timer>0){timer--;}
Or something like that. You see what I'm saying, right? Make it so that it can only reflect once in a ten frame interval. (I'm still new to tasks, so I'm not 100% on the whole yield setup inside a 'while' loop)
Title: Re: Another Danmakufu Tutorial?
Post by: kdillon7323 on April 25, 2009, 11:12:13 PM
Hi i'm kind of new to danmakufu and to the forum but I have been reading since blargels tutorial and I would like to ask a question.

I don't know how to work if I wanted to fire 2 different bullets and one at 60 frames and then the other at 120 and then it fires back and forth.  How would that script look like??
thank you in advance.
Title: Re: Another Danmakufu Tutorial?
Post by: Hat on April 25, 2009, 11:50:43 PM
Code: [Select]
#TouhouDanmakufu
#Title[Test Sign "Test"]
#Text[Test script]
#Player[FREE]
#ScriptVersion[2]

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

    let frame = -120;

    @Initialize {
        SetLife(2000);
        SetTimer(50);
        SetScore(1000000);

        SetMovePosition02(GetCenterX, GetClipMinY + 120, 120);
        CutIn(YOUMU, "Test Sign "\""Test"\", "", 0, 0, 0, 0);

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

    @MainLoop {
        SetCollisionA(GetX, GetY, 32);
        SetCollisionB(GetX, GetY, 16);
        SetShotDirectionType(PLAYER);
        if(frame == 0) {
            CreateShot01(GetX + 15, GetY, 5, 0, RED01, 0);
        }
        if(frame == 60) {
           CreateShot01(GetX - 15, GetY, 5, 0, BLUE01, 0);
        }
        if(frame == 120) {
          frame = -1;
        }
        frame++;
    }

    @DrawLoop {
        DrawGraphic(GetX, GetY);
    }

    @Finalize {
        DeleteGraphic(ImgBoss);
    }
}
Comprende? At frame = 0, it fires one shot, then at frame = 60 it fires another shot... then 60 frames later, at frame = 120, it loops back (frame = -1 is just so that the frame++ doesn't accidentally skip over the first frame == 0 argument).

The whole SetShotDirectionType deal is to account for the fact that GetAngleToPlayer gets the angle to the player from the perspective of the enemy's sprite. Since I decided to be stupid and made the balls shoot a little to the right and left of Rumia, they'd actually graze you without ever hitting you. SetShotDirectionType sets the '0' of the coordinate system at the angle to the player relative to the BULLET, so that doesn't happen.
Title: Re: Another Danmakufu Tutorial?
Post by: kdillon7323 on April 26, 2009, 12:50:08 AM
Ok thank you I now understand and that helps a lot.
Title: Re: Another Danmakufu Tutorial?
Post by: Garlyle on April 27, 2009, 02:57:35 PM
Quote
Just don't forget to insert a yield command in your MainLoop, or the bullet won't bounce.
This should be in big bold letters.

Also

while(!Obj_BeDeleted){} is just a short form of "while Obj_BeDeleted==false" .  ! means 'not' in programming terms.  For instance, if(bullets != 30){} would activate when 'bullets' was -not- equal to 30.  When checking for boolean values, like Obj_BeDeleted, you don't even need the == if it's searching for true.  The ! beforehand means "when NOT".

So yeah.  They're the same thing, just !Obj_BeDeleted is a bit quicker.


(Also, I may take the time to write up a tutorial on stages/events, so...)
Title: Re: Another Danmakufu Tutorial?
Post by: Iryan on April 27, 2009, 05:22:53 PM
Sooo... I said I wanted to show how to replicate AyaSatori's 3rd spellcard, "Great Whirlwind". It was actually really easy, the typing of this post will probably take much longer. :V


How to use object bullets part 2: "Great Whirlwind"

(http://images4.wikia.nocookie.net/touhou/images/e/ef/Th11sc080.jpg)

So, you are familiar with this spellcard and maybe even captured it. But how does it work?
Well, each seperate cyclone is a cluster of several bullets with the same starting point and angular velocity, but with different starting angles and speeds. The same angular velocity ensures that every bullet of the cluster will return to the center at the same time, then the cluster will unfold again, dependent on the different angles and speeds of the individual bullets.
So far, this would be easy to replicate with a CreateShotA, right? There is a little problem, however: the cyclones move downward, and still, they manage to stick to their circling motion. That is quite tougher to program.

There are actually two ways to do this; one involves CreateShotA_XY and trigonometric functions, the other involves object bullets, but since I'm here to showcase how to have fun with object bullets, we'll use these.

Let's start out with the basic framework from the previous post, with a different bullet sprite:
Code: [Select]
task Bullet(x, y, v, angle) {
      let obj=Obj_Create(OBJ_SHOT);

      Obj_SetPosition(obj, x, y);
      Obj_SetAngle(obj, angle);
      Obj_SetSpeed(obj, v);
      ObjShot_SetGraphic(obj, BLUE21);
      ObjShot_SetDelay  (obj, 0);
      ObjShot_SetBombResist (obj, true);

      while(Obj_BeDeleted(obj)==false) {
              yield;
       }
We said that the bullets have an angular velocity, so we'll work in a command that adjusts the angle of the bullet by a fixed amount every frame:
Code: [Select]
      Obj_SetAngle(obj, Obj_GetAngle(obj) + 2);
Now we can create an object bullet that will move in a circle. Firing a ring of these bullets will get you a pulsating circle with a radius of
360*(velocity of the bullets) / (2*Pi).
Using an angular veloctiy of 2 means that the bullet will have moved a full circle after 360/2 frames. That is exactly three seconds after being fired.

The bullets still won't move downwards, though. To accomplish that, we have to add a command that moves the bullet downward every frame, independently from the regular movement direction and speed. To simulate this movement, we can simply set the position of the bullet to a place the has a distance of, say, one pixel, every frame. A command that does this looks like this:
Code: [Select]
      Obj_SetPosition(Obj_GetX(obj), Obj_GetY(obj) + 1);
The bullet is moved straight down by one pixel every frame. Of course, if we wanted, we could make the whole cyclone move in a specific pattern itself, like waving to left and right while moving to the bottom of the screen ~~~~ , adjusting the x coordinate every second dependent on the value of a trigonometric function. You can do that as practice, if you like.  ;D

So, inserting both of these commands into the bullet task, we get:

Code: [Select]
task Bullet(x, y, v, angle) {
      let obj=Obj_Create(OBJ_SHOT);

      Obj_SetPosition(obj, x, y);
      Obj_SetAngle(obj, angle);
      Obj_SetSpeed(obj, v);
      ObjShot_SetGraphic(obj, BLUE21);
      ObjShot_SetDelay  (obj, 0);
      ObjShot_SetBombResist (obj, true);

      while(Obj_BeDeleted(obj)==false) {
              Obj_SetAngle(obj, Obj_GetAngle(obj) + 2);
              Obj_SetPosition(Obj_GetX(obj), Obj_GetY(obj) + 1);
              yield;
       }

This task creates a single bullet that spirals down the screen. To craft a cyclone, you have to fire a ring of these bullets. Several rings with different bullet speeds, actually. For this, we can define a function:

Code: [Select]
function cyclone(x, y){
        ascent(i in 0..10){
        ascent(k in 0..6){
                Bullet(x, y, 0.5*(1+k), i*40);
        }    }
This will fire 6 rings of 9 stormbullets each. The rings have an individual bullet speed of 0.5, 1, 1.5,  2, 2.5 and 3, and thus different radii. Calling This function for a specified place will call down a whirlwind that folds and unfolds indefinitely as it carves it's way to the bottom of the screen.

Now, to emulate the spellcard, define the task after @finalize, define the function in the mainscript and then call the function with your main loop in regular intervals.
Again, do not forget the yield; in the MainLoop!
Title: Re: Another Danmakufu Tutorial?
Post by: Hat on April 27, 2009, 11:46:15 PM
Wow, Iryan, you're really good at this object bullet business. I was actually meaning to figure this one out independently, but thanks anyway!
Title: Re: Another Danmakufu Tutorial?
Post by: kdillon7323 on April 28, 2009, 05:57:55 PM
could I ask how after Kurodani Yamames first spell card how to make the bullets kinda spin around her while shooting? I dont care about them turning red and then slowing down just the spinning part.
Title: Re: Another Danmakufu Tutorial?
Post by: Iryan on April 28, 2009, 07:43:42 PM
You mean spiraling bullets?

That is actually the second most commmon way to create a pattern of danmaku, after the bulletrings.

To do that, you just have to fire a bullet each frame with any CreateShot, the direction of which is determined by a variable. Then have this variable increase by a set amount every frame, and looky there - a spiral of bullets.

Yamame's 2nd nonspellcard has several of these spirals that are divided evenly among 360 degrees. Just fire, let's say 4 bullets, one at an angle of (0+variable), one at an angle of (90+variable), one at an angle of (180+variable) and one at an angle of (270+variable).

There you go.  8)
Title: Re: Another Danmakufu Tutorial?
Post by: kdillon7323 on April 28, 2009, 08:04:39 PM
Ok I can get one to spiral.
But when I try to get 2 to spiral in opposite directions I have trouble

if(frame==10){
CreateShot01(GetX, GetY, 2, angle, RED21, 0);
angle += 17;
CreateShot01(GetX, GetY, 2, angle, BLUE21, 0);
angle -= 17;
frame = 0;}
frame++

frame and angle are both 0.
The red looks like its shooting at 0 degrees and the blue looks like its shooting at about 20.
How do I fix this?
Title: Re: Another Danmakufu Tutorial?
Post by: Garlyle on April 28, 2009, 08:46:12 PM
Use different variables.

Your angle commands are cancelling each other out.  It's going...

"Fire RED at angle (0)
Increase angle by 17 (now 17)
Fire BLUE at angle (17)
Decrease angle by 17 (now 0)"

You could try having two variables, like so:

CreateShot01(GetX, GetY, 2, angleA, RED21, 0);
angleA += 17;
CreateShot01(GetX, GetY, 2, angleB, BLUE21, 0);
angleB -= 17;

Alternatively, if you don't want to use different variables, try the following:

CreateShot01(GetX, GetY, 2, angle, RED21, 0);
CreateShot01(GetX, GetY, 2, 0-angle, BLUE21, 0);
angle += 17;

This will basically make the two go in opposite directions.  Different methods of doing this make different results, of course.
Title: Re: Another Danmakufu Tutorial?
Post by: Iryan on April 28, 2009, 08:51:15 PM
Well, of course.

Read your code again and think about what it does. It fires one bullet at an angle of "angle", which starts 0, then increases "angle" by 17, then fires a bullet at "angle", which is 17 (and thus basically is "about 20"), then changes "angle" back to 0. Next loop, it starts again the same way.

You have two options. Either you make two different variables for the different spirals' angles, or you make the second bullet be fired at an angle of (-"angle").


I advise you to closely examine your code yourself everytime something is not working at fixing it yourself. That way, you'll gain a better understanding of what happens why, and what doesn't happen why: it makes you a better programmer.  ;)


?dit: Darn, lunardial'd!
Title: Re: Another Danmakufu Tutorial?
Post by: kdillon7323 on April 28, 2009, 09:13:47 PM
I should have thought of the two different variable part lol.
But I got it to work.
Ok another question...(apoligizing for a whole lot of questions.)


I have this
if(frame==5){
CreateShot01(GetX, GetY, 2, angle, RED01, 1);
angle+=3;
CreateShot01(GetX, GetY, 2, angle+180, BLUE01, 1);
angle+=3;
frame = 0;}
frame++

So the bullets are spiraling firing every 5 frames.
But say I want to fire a ring of bullets every 60 frames...
but the frame never gets past 3... How do I get THAT to work?
Title: Re: Another Danmakufu Tutorial?
Post by: Hat on April 28, 2009, 10:05:28 PM
Either use another variable for the framecount, OR in this particular instance you could say instead of
Code: [Select]
if(frame==5){
CreateShot01(GetX, GetY, 2, angle, RED01, 1);
angle+=3;
CreateShot01(GetX, GetY, 2, angle+180, BLUE01, 1);
angle+=3;
frame = 0;}
frame++
You could have,
Code: [Select]
if(frame%5 == 0){
CreateShot01(GetX, GetY, 2, angle, RED01, 1);
angle+=3;
CreateShot01(GetX, GetY, 2, angle+180, BLUE01, 1);
angle+=3;
frame = 0;}
if(frame%60 == 0){
Some Bullets Hizurr
frame = 0;
}
frame++
Something like that. The modulus % means, basically... if you were to divide it by the number indicated after the %, the remainder is given. So every five frames, the remainder will be zero... and you can count up that way. Once the modulus for 60 is passed, it'll set it back to 0 and you start the whole process over again.
Title: Re: Another Danmakufu Tutorial?
Post by: kdillon7323 on April 28, 2009, 10:30:33 PM
OK thank you I got it to work with the % modulus but I deleted "frame = 0" after the 2nd CreateShot01.
Title: Re: Another Danmakufu Tutorial?
Post by: Hat on April 28, 2009, 10:35:14 PM
AH, yes, I knew I was forgetting something. XD Derp derp.
Title: Re: Another Danmakufu Tutorial?
Post by: Naut on May 13, 2009, 04:26:27 AM
What does SetMovePositionHermite(); do, what are it's parameters, how does it work, and is it even worth my time including it in the Boss Movement section of this intermediate tutorial? I know it's explained in the Japanese tutorial with a diagram and pretty arrows, which seem to yield a lovely curved movement trajectory... But man, everytime I go to the Engrish Danmakufu wiki for a synopsis of what's going on I just rage furiously at that fucking useless explanation! Blaarargsbhfghh!
Title: Re: Another Danmakufu Tutorial?
Post by: Iryan on May 13, 2009, 10:13:05 AM
The way I understand it from the wiki, it goes as follows:

The boss starts to move from his position. The motion starts in a specified direction d1 with a specified speed v1.
The boss moves towards the specified position, that is, ( x | y ). At the moment when he arrives, his movement has a certain angle d2 and he moves at a speed of v2.

The entire motion in between will be calculated as a curve so that the initial movement angle and the movement angle at the arrival are as specified. Ditto for initial and final speed. The speed in general will be adjusted so that initial and final speed are as specified and the total durance of the movement is the specified time t.

Thus, if I understand it correctly, the parameters would be in order: (x, y, v1, d1, v2, d2, t)

Sounds like it would eat on your processor in a similar way as the curving lasers...

I'd test it, but I'm not in the mood to restart my PC right now...
Title: Re: Another Danmakufu Tutorial?
Post by: CK Crash on May 13, 2009, 09:13:21 PM
How would I go about actually making a spellcard background? I also need it to fade in as the spellcard starts, since I plan on making it part of a plural script.

I realize this may be better for the Q/A thread, but this is also something that ought to be covered by the tutorials better.
Title: Re: Another Danmakufu Tutorial?
Post by: Garlyle on May 13, 2009, 10:37:04 PM
Include an @Background section on the Spellcard.  Each individual Spellcard will utilize the background you specify within instead of the default, if you have included it.
Title: Re: Another Danmakufu Tutorial?
Post by: Stuffman on May 13, 2009, 11:05:47 PM
Quote
What does SetMovePositionHermite(); do, what are it's parameters, how does it work, and is it even worth my time including it in the Boss Movement section of this intermediate tutorial?

Ilyan's explanation is mostly right but not quite.

Code: [Select]
SetMovePositionHermite(x,y,distance1,angle1,distance2,angle2,frames);
As a sample from one of my spinner enemies in PoSR:
Code: [Select]
SetMovePositionHermite(GetX()+100,GetY(),300,90,300,270,40);The enemy comes onto the screen straight down and does a 180 to its right. The helmite part of its movement involves moving 100 pixels to the right, starting at 90 degrees (down) and ending at 270 degrees (up). The distance in both cases is 300 (3*100 pixel distance) for a round movement arc. It takes 40 frames to complete this movement.

That should be enough for practical use, but you can get some really wacky movement if you play around with it. It's actually rather easy to use once you understand the arguments. The distance arguments are the only ones I don't fully understand.

And no it doesn't cause any slowdown, the slowdown on C Lasers is because of the complex transparent drawing involved, not the math.

---

Quote
How would I go about actually making a spellcard background? I also need it to fade in as the spellcard starts, since I plan on making it part of a plural script.

I actually don't even bother with @Background, I just draw a screen-size image behind the boss in @DrawLoop. You can even have multiple images with different transparency for layered effects. Just draw it before you draw the boss so the boss is on top of it. For the fade-in just have a SetAlpha with a special count variable or whatever. It works exactly the same way as drawing the boss, just much bigger.
Title: Re: Another Danmakufu Tutorial?
Post by: Frazer on May 13, 2009, 11:20:14 PM
Why is it that whenever I try to call a task/function/whatever, the name of said function in the MainLoop always comes up as an error?

Take this for example:


Code: [Select]
#TouhouDanmakufu
#Title[Bounce Bullet]
#Text[Test script]
#Player[FREE]
#ScriptVersion[2]

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

let count = 0;
let a=0;

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

SetEnemyMarker(true);

SetMovePosition02(225, 120, 60);

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

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

if(count == 5) {
Bounce(GetX, GetY, 3, GetAngleToPlayer);

count=0
}
count++;
}

@DrawLoop {
DrawGraphic(GetX, GetY);
}

@Finalize {
DeleteGraphic(ImgBoss);
}
}







task Bounce(x, y, v, angle) {
     let obj=Obj_Create(OBJ_SHOT);

     Obj_SetPosition(obj, x, y);
     Obj_SetAngle(obj, angle);
     Obj_SetSpeed(obj, v);
     ObjShot_SetGraphic(obj, RED01);
     ObjShot_SetDelay(obj, 0);
     ObjShot_SetBombResist(obj, true);

     while(Obj_BeDeleted(obj)==false){

           yield;

}

What am I doing wrong? Why does it keep reading it as an error? I don't get it.
Title: Re: Another Danmakufu Tutorial?
Post by: Stuffman on May 13, 2009, 11:56:32 PM
The task needs to be inside script_enemy_main. Put it above @Initialize or something.
Title: Re: Another Danmakufu Tutorial?
Post by: Frazer on May 14, 2009, 12:06:36 AM
Thank you.

Why does Iryan say put it after finalize?
Title: Re: Another Danmakufu Tutorial?
Post by: Naut on May 14, 2009, 12:20:04 AM
It can go anywhere you want, as long as it's inside script_enemy_main{}, but outside any @MainLoop{}, @Finalize, @Initialize, etc. Most people put tasks either before @Initialize (which would mean at the start of the script), or after @Finalize (which would mean at the end of the script). These places are typically where people look for any tasks you've defined, as most of us don't want to sift through the middle of your code for any tasks you've hidden away. It doesn't affect anything except how easy it is for a human to find in your text file, so don't worry about it.

Also, thanks for the explanations on SetMovePositionHermite, I'll be including it in the tutorial.
Title: Re: Another Danmakufu Tutorial?
Post by: Stuffman on May 14, 2009, 12:29:54 AM
Useless but interesting: Actually, I think you can put tasks inside things like @Initialize or @MainLoop, but then it'll only work inside of them. It works like declaring variables in that respect.
Title: Re: Another Danmakufu Tutorial?
Post by: Frazer on May 14, 2009, 12:38:35 AM
Okay.


I have one more question: Does anybody know how to detect object collision with other objects (i.e. say:

Quote
if(object collision == true){
)


Touhou wiki's Collision_Obj_Obj is being read as an error for me.
Title: Re: Another Danmakufu Tutorial?
Post by: Naut on May 14, 2009, 12:40:38 AM
Would putting tasks inside @MainLoop or something affect the yield; command in any way? Because you'd be processing everything in the MainLoop, including the task, right? So yield would...
...
Hmm, probably not, but I wouldn't want to mess around with it. They work just dandy outside any @Shit{}, so that's where I'll be keeping them, haha!



Frazer: What's your script look like?
Title: Re: Another Danmakufu Tutorial?
Post by: Frazer on May 14, 2009, 12:54:33 AM
Code: [Select]
#TouhouDanmakufu
#Title[Bounce Bullet]
#Text[Test script]
#Player[FREE]
#ScriptVersion[2]

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

let count = 540;
let a=0;





task Bounce(x, y, v, angle) {
     let obj=Obj_Create(OBJ_SHOT);

     Obj_SetPosition(obj, x, y);
     Obj_SetAngle(obj, angle);
     Obj_SetSpeed(obj, v);
     ObjShot_SetGraphic(obj, RED01);
     ObjShot_SetDelay(obj, 0);
     ObjShot_SetBombResist(obj, true);
     Obj_SetCollisionToObject(obj, true);

     while(Obj_BeDeleted(obj)==false){

           if(Obj_GetX(obj)<GetClipMinX) {
                Obj_SetAngle(obj, 180 - Obj_GetAngle(obj) ) ;
                Obj_SetX(obj, Obj_GetX(obj) + 0.1);
           }

           if(Obj_GetX(obj)>GetClipMaxX) {
                Obj_SetAngle(obj, 180 - Obj_GetAngle(obj) );
                Obj_SetX(obj, Obj_GetX(obj) - 0.1);
           }

           if(Obj_GetY(obj)<GetClipMinY) {
                Obj_SetAngle(obj, 0 - Obj_GetAngle(obj) ) ;
                Obj_SetY(obj, Obj_GetY(obj) + 0.1);
           }

           if(Obj_GetY(obj)>GetClipMaxY) {
                Obj_SetAngle(obj, 0 - Obj_GetAngle(obj) );
                Obj_SetY(obj, Obj_GetY(obj) - 0.1);
           }

           if(Collision_Obj_Obj(obj, obj)){
           Obj_SetSpeed(obj, -v)
           }

           yield;
}
}







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

SetEnemyMarker(true);

SetMovePosition02(225, 120, 60);

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

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

if(count == 600) {
loop(20){
Bounce(GetX, GetY, 5, a);

a += 360/20;
}

count=0
}
count++;
}

@DrawLoop {
DrawGraphic(GetX, GetY);
}

@Finalize {
DeleteGraphic(ImgBoss);
}
}




The bullets do not collide with each other. Do the bullets really need to be two different objects to collide with each other, or is it possible to have the bullets collide with only one ID?
Title: Re: Another Danmakufu Tutorial?
Post by: Garlyle on May 14, 2009, 02:27:47 AM
That setup won't work.

Each bullet instance (as Obj) is existing only within its personal instance of Bounce.  Each possible bullet would need to be registered as a unique 'let obj' within the main script, and commanded separately...

ugh, not quite sure how to say it, but basically, you'd want multiple 'let obj01' type lines, but you wouldn't necessarily need separate code...

long story short, b**** is ugly.
Title: Re: Another Danmakufu Tutorial?
Post by: Stuffman on May 14, 2009, 02:33:35 AM
I haven't really messed with the object collision functions either. We need to figure that crap out at some point.
Title: Re: Another Danmakufu Tutorial?
Post by: Naut on May 14, 2009, 04:09:03 AM
Consensus ad idem, Danmakufu style. I might diddle with it over the weekend if I find time, anyway... That is, if you guys don't pop it's cherry first.
Title: Re: Another Danmakufu Tutorial?
Post by: Garlyle on May 14, 2009, 05:26:03 AM
Collision isn't too hard, just ensure declaration of 'reflector' (etc.) Objects are both A) Unique and B) public (aka the 'let' text is in normal variable location, not a task/loop/etc. )

I'll try?

EDIT: 8D Okay, does odd things once in a while, but... :3
Code: [Select]
#TouhouDanmakufu
#Title[Mirror Sign 「Border Trackers」]
#Text[Testing obj collision]
#BackGround[Default]
#BGM[.\bgm.mp3]
#PlayLevel[Test]
#Player[FREE]
#ScriptVersion[2]

script_enemy_main {
    let frame = -120;
    let arc = 0.5;
    let cx=GetCenterX();
    let minx=GetClipMinX;
    let maxx=GetClipMaxX;
    let miny=GetClipMinY;
    let maxy=GetClipMaxY;
    let cy=GetCenterY();
    let imgExRumia="script\img\ExRumia.png";
    let BossCutIn = "player\Rumia\RumiaCutIn.png";
    let SEShot = GetCurrentScriptDirectory~"sound\Eff1.wav";
    let objLaz1=Obj_Create(OBJ_LASER);
    let objLaz2=Obj_Create(OBJ_LASER);

task BoundShot(x, y, v, angle) {
      let obj=Obj_Create(OBJ_SHOT);
      Obj_SetPosition(obj, x, y);
      Obj_SetAngle(obj, angle);
      Obj_SetSpeed(obj, v);
      ObjShot_SetGraphic(obj, RED01);
      ObjShot_SetDelay  (obj, 10);
      ObjShot_SetBombResist (obj, true);
      while(Obj_BeDeleted(obj)==false) {
              if(Collision_Obj_Obj(obj,objLaz1)) {
                     Obj_SetAngle(obj,180 - Obj_GetAngle(obj) );
                     Obj_SetX(obj,Obj_GetX(objLaz1) + 5);
              }
              if(Collision_Obj_Obj(obj,objLaz2)) {
                     Obj_SetAngle(obj, 180 - Obj_GetAngle(obj) );
                     Obj_SetX(obj,Obj_GetX(objLaz2) - 5);
              }
              if(Obj_GetY(obj) < 0){
                     Obj_SetY(obj,10);
                     Obj_SetAngle(obj,180-Obj_GetAngle(obj));
              }
              yield;
       }
}

task Lasercage1() {
      Obj_SetAngle(objLaz1, 90);
      ObjLaser_SetLength(objLaz1, maxx+50);
      ObjLaser_SetWidth(objLaz1, 7);
      ObjShot_SetDelay(objLaz1, 60);
      ObjShot_SetGraphic(objLaz1, WHITE01);
      Obj_SetSpeed(objLaz1, 0);
      Obj_SetY(objLaz1, 0);
      while(Obj_BeDeleted(objLaz1)==false) {
            if(GetPlayerX-120 > minx){
                  Obj_SetX(objLaz1, GetPlayerX-120);
            }else{
                  Obj_SetX(objLaz1, minx);
            }
            Obj_SetCollisionToObject(objLaz1, true);
            ObjShot_SetBombResist(objLaz1, true);
            yield;
      }
}
task Lasercage2() {
      Obj_SetAngle(objLaz2, 90);
      ObjLaser_SetLength(objLaz2, maxx+50);
      ObjLaser_SetWidth(objLaz2, 7);
      ObjShot_SetDelay(objLaz2, 60);
      ObjShot_SetGraphic(objLaz2, WHITE01);
      Obj_SetSpeed(objLaz2, 0);
      Obj_SetY(objLaz2, 0);
      while(Obj_BeDeleted(objLaz1)==false) {
            if(GetPlayerX+120 < maxx){
                  Obj_SetX(objLaz2, GetPlayerX+120);
            }else{
                  Obj_SetX(objLaz2, maxx);
            }
            Obj_SetCollisionToObject(objLaz2, true);
            ObjShot_SetBombResist(objLaz2, true);
            yield;
      }
}

    @Initialize {
        SetLife(4000);
SetDamageRate(100,80);
SetTimer(50);
        SetInvincibility(120);
Concentration01(120);
        SetEnemyMarker(true);
        SetScore(1000000);
        SetEffectForZeroLife(120,51,1);
        LoadGraphic(imgExRumia);
        SetGraphicRect(1,1,64,64);
        CutIn(YOUMU, "Mirror Sign 「Border Trackers」", BossCutIn, 0, 0, 200, 600);
    }

    @MainLoop {
        SetCollisionA(GetX, GetY, 32);
        SetCollisionB(GetX, GetY, 24);
        SetY(90);
        SetX(GetPlayerX);
        SetShotAutoDeleteClip(175,64,175,64);
if (frame==-60){
Lasercage1;
Lasercage2;
}
frame++;
        if (frame==0){
                BoundShot(GetX,GetY,2.5,arc);
                BoundShot(GetX,GetY,2.5,arc+90);
                BoundShot(GetX,GetY,2.5,arc+180);
                BoundShot(GetX,GetY,2.5,arc+270);
                arc += rand_int(8,12);
        }
if (frame==9){
frame = -1;
}
        yield;
    }

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

    @Finalize {
loop(8){
CreateItem(ITEM_SCORE,cx+rand(-100,100),rand(20,100));
}
DeleteGraphic(imgExRumia);
    }

    @BackGround {
    }
}
Title: Re: Another Danmakufu Tutorial?
Post by: Iryan on May 14, 2009, 09:46:06 AM
Thank you.

Why does Iryan say put it after finalize?
Because I put it there everytime and because every script that uses tasks and that I've looked at does it. When I say "after the @Finalize part", I mean "after the closing bracket of your @ Finalize script". I don't mean "After the stuff after the closing bracket of the @Finalize part which would be the closing bracket of your enitre script thus causing the task not to be included in the script at all". One bracket makes all the difference.  ;D


On the bouncing, I've done a spellcard where three objects bounce inside a cage, it's not that difficult. Bullets bouncing off of each other, well, as has been said you'd need to have all the bullets be on seperate IDs, which is troublesome if you want a high amount of bullets. The real problem though would be to make the bullets bounce in a realistic fashion. I've thought about it, and to really emulate a bounce that feels "right", providing that all bullets move at the same speed, a real bounce is imitated by setting the angle of both bullets to the angle of the other one. To do that, You have to store the angle of one bullet in a variable, then copy the other bullet's angle, than change the other bullet's angle to the value of the variable. One has to be careful, though. If both bullets have this ability, they will cancel each other out. Thus every bullet pair must have only one instance of this check. Which one it is bound to is obsolete. Hell, as you probably need different tasks for each bullet, you sould be just fine putting all the bounce controllers in the main loop!

So yeah...

On another note, is there a way to have the same task spit out objects of different names which depend on a parameter so that you'd need only one task for all these interacting bullets? Something that involves converting stuff into strings with a command? Or the other way round? It could save pages of code and time.
Title: Re: Another Danmakufu Tutorial?
Post by: Garlyle on May 15, 2009, 12:40:25 AM
Quote
On another note, is there a way to have the same task spit out objects of different names which depend on a parameter so that you'd need only one task for all these interacting bullets? Something that involves converting stuff into strings with a command? Or the other way round? It could save pages of code and time.

Arrays.  I don't wanna wrap my brain around that right now, too busy enjoying Uwabami Breakers.
Title: Re: Another Danmakufu Tutorial?
Post by: Nuclear Cheese on May 15, 2009, 01:36:17 AM
On another note, is there a way to have the same task spit out objects of different names which depend on a parameter so that you'd need only one task for all these interacting bullets? Something that involves converting stuff into strings with a command? Or the other way round? It could save pages of code and time.

Arrays.  I don't wanna wrap my brain around that right now, too busy enjoying Uwabami Breakers.

If you check in my scripts thread (http://www.shrinemaiden.org/forum/index.php?topic=216.0), you can check out Random Sign 「Entropy」 ...

This spellcard was made to demonstrate using arrays to keep track of several object shots (and give a glimpse of what truely random danmaku could look like :V ).  I tried to comment it heavily enough that it can help you learn how to handle it.

If not, feel free to ask and I can give a more in-depth explaination.

BTW - you don't need a task to handle this.  Like I did in the above spellcard, it can be all handled in @MainLoop.  That's my preferred style, but either way will work fine.

On the bouncing, I've done a spellcard where three objects bounce inside a cage, it's not that difficult. Bullets bouncing off of each other, well, as has been said you'd need to have all the bullets be on seperate IDs, which is troublesome if you want a high amount of bullets. The real problem though would be to make the bullets bounce in a realistic fashion. I've thought about it, and to really emulate a bounce that feels "right", providing that all bullets move at the same speed, a real bounce is imitated by setting the angle of both bullets to the angle of the other one. To do that, You have to store the angle of one bullet in a variable, then copy the other bullet's angle, than change the other bullet's angle to the value of the variable. One has to be careful, though. If both bullets have this ability, they will cancel each other out. Thus every bullet pair must have only one instance of this check. Which one it is bound to is obsolete. Hell, as you probably need different tasks for each bullet, you sould be just fine putting all the bounce controllers in the main loop!

I did "bullets bouncing off of lasers" in another spellcard in my post, Light Sign 「Reflection/Refraction」.

It's not commented as well as Entropy, but hopefully you can get something useful out of it.  It handles object-to-object collision detection, and determines the angle of the bullet being reflected off of the laser.
(It also, by the way, refracts the same bullets through the lasers when they collide)

Its not exactly what you're referring to, but it might give some help.
Title: Re: Another Danmakufu Tutorial?
Post by: Frazer on May 15, 2009, 02:24:33 AM
Collision isn't too hard, just ensure declaration of 'reflector' (etc.) Objects are both A) Unique and B) public (aka the 'let' text is in normal variable location, not a task/loop/etc. )

I'll try?

EDIT: 8D Okay, does odd things once in a while, but... :3
Code: [Select]
#TouhouDanmakufu
#Title[Mirror Sign 「Border Trackers」]
#Text[Testing obj collision]
#BackGround[Default]
#BGM[.\bgm.mp3]
#PlayLevel[Test]
#Player[FREE]
#ScriptVersion[2]

script_enemy_main {
    let frame = -120;
    let arc = 0.5;
    let cx=GetCenterX();
    let minx=GetClipMinX;
    let maxx=GetClipMaxX;
    let miny=GetClipMinY;
    let maxy=GetClipMaxY;
    let cy=GetCenterY();
    let imgExRumia="script\img\ExRumia.png";
    let BossCutIn = "player\Rumia\RumiaCutIn.png";
    let SEShot = GetCurrentScriptDirectory~"sound\Eff1.wav";
    let objLaz1=Obj_Create(OBJ_LASER);
    let objLaz2=Obj_Create(OBJ_LASER);

task BoundShot(x, y, v, angle) {
      let obj=Obj_Create(OBJ_SHOT);
      Obj_SetPosition(obj, x, y);
      Obj_SetAngle(obj, angle);
      Obj_SetSpeed(obj, v);
      ObjShot_SetGraphic(obj, RED01);
      ObjShot_SetDelay  (obj, 10);
      ObjShot_SetBombResist (obj, true);
      while(Obj_BeDeleted(obj)==false) {
              if(Collision_Obj_Obj(obj,objLaz1)) {
                     Obj_SetAngle(obj,180 - Obj_GetAngle(obj) );
                     Obj_SetX(obj,Obj_GetX(objLaz1) + 5);
              }
              if(Collision_Obj_Obj(obj,objLaz2)) {
                     Obj_SetAngle(obj, 180 - Obj_GetAngle(obj) );
                     Obj_SetX(obj,Obj_GetX(objLaz2) - 5);
              }
              if(Obj_GetY(obj) < 0){
                     Obj_SetY(obj,10);
                     Obj_SetAngle(obj,180-Obj_GetAngle(obj));
              }
              yield;
       }
}

task Lasercage1() {
      Obj_SetAngle(objLaz1, 90);
      ObjLaser_SetLength(objLaz1, maxx+50);
      ObjLaser_SetWidth(objLaz1, 7);
      ObjShot_SetDelay(objLaz1, 60);
      ObjShot_SetGraphic(objLaz1, WHITE01);
      Obj_SetSpeed(objLaz1, 0);
      Obj_SetY(objLaz1, 0);
      while(Obj_BeDeleted(objLaz1)==false) {
            if(GetPlayerX-120 > minx){
                  Obj_SetX(objLaz1, GetPlayerX-120);
            }else{
                  Obj_SetX(objLaz1, minx);
            }
            Obj_SetCollisionToObject(objLaz1, true);
            ObjShot_SetBombResist(objLaz1, true);
            yield;
      }
}
task Lasercage2() {
      Obj_SetAngle(objLaz2, 90);
      ObjLaser_SetLength(objLaz2, maxx+50);
      ObjLaser_SetWidth(objLaz2, 7);
      ObjShot_SetDelay(objLaz2, 60);
      ObjShot_SetGraphic(objLaz2, WHITE01);
      Obj_SetSpeed(objLaz2, 0);
      Obj_SetY(objLaz2, 0);
      while(Obj_BeDeleted(objLaz1)==false) {
            if(GetPlayerX+120 < maxx){
                  Obj_SetX(objLaz2, GetPlayerX+120);
            }else{
                  Obj_SetX(objLaz2, maxx);
            }
            Obj_SetCollisionToObject(objLaz2, true);
            ObjShot_SetBombResist(objLaz2, true);
            yield;
      }
}

    @Initialize {
        SetLife(4000);
SetDamageRate(100,80);
SetTimer(50);
        SetInvincibility(120);
Concentration01(120);
        SetEnemyMarker(true);
        SetScore(1000000);
        SetEffectForZeroLife(120,51,1);
        LoadGraphic(imgExRumia);
        SetGraphicRect(1,1,64,64);
        CutIn(YOUMU, "Mirror Sign 「Border Trackers」", BossCutIn, 0, 0, 200, 600);
    }

    @MainLoop {
        SetCollisionA(GetX, GetY, 32);
        SetCollisionB(GetX, GetY, 24);
        SetY(90);
        SetX(GetPlayerX);
        SetShotAutoDeleteClip(175,64,175,64);
if (frame==-60){
Lasercage1;
Lasercage2;
}
frame++;
        if (frame==0){
                BoundShot(GetX,GetY,2.5,arc);
                BoundShot(GetX,GetY,2.5,arc+90);
                BoundShot(GetX,GetY,2.5,arc+180);
                BoundShot(GetX,GetY,2.5,arc+270);
                arc += rand_int(8,12);
        }
if (frame==9){
frame = -1;
}
        yield;
    }

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

    @Finalize {
loop(8){
CreateItem(ITEM_SCORE,cx+rand(-100,100),rand(20,100));
}
DeleteGraphic(imgExRumia);
    }

    @BackGround {
    }
}



Thank you very much for the information.
Title: Re: Another Danmakufu Tutorial?
Post by: Chronojet ⚙ Dragon on July 25, 2009, 08:33:41 PM
Quote
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

How about System.png, SystemText.png and Effect.png? What do those things change?
Title: Re: Another Danmakufu Tutorial?
Post by: Drake on July 25, 2009, 09:04:38 PM
I'm not sure. I've been hearing they do do something though, so I'll have to check it out some time.
Title: Re: Another Danmakufu Tutorial?
Post by: Helepolis on July 26, 2009, 12:31:11 PM
Where do you have to place these anyway?
Title: Re: Another Danmakufu Tutorial?
Post by: Zengar Zombolt on July 26, 2009, 03:22:13 PM
Where do you have to place these anyway?
The img folder, that's on the main danmakufu folder as well. Normally it doesn't exist, because you're using all the default images.
Title: Re: Another Danmakufu Tutorial?
Post by: Helepolis on July 26, 2009, 04:50:38 PM
So that is how people modify the screen images.

edit: Well yea, the sounds go in "se" folder  (create it inside the root). I added Marisa's Masterspark sound and it works very nice. seBomb_MarisaB.wav

This will be a nice touchup for my v3 of Dancecontest as I plan to add sprites for Marisa aswell. White Marisa looks a bit =.=
Title: Re: Another Danmakufu Tutorial?
Post by: Chronojet ⚙ Dragon on July 31, 2009, 04:51:21 AM
So that is how people modify the screen images.

edit: Well yea, the sounds go in "se" folder  (create it inside the root). I added Marisa's Masterspark sound and it works very nice. seBomb_MarisaB.wav

This will be a nice touchup for my v3 of Dancecontest as I plan to add sprites for Marisa aswell. White Marisa looks a bit =.=

I put -THIS- in my img folder.  ;)
So far, the "Ten" bar obscures part of the #PlayLevel name. :(
Anyways...
Title: Re: Another Danmakufu Tutorial?
Post by: Helepolis on July 31, 2009, 05:37:12 AM
That I already have, a different framework. But I don't want to show it yet.

We should add these together with Pikaguy's stuff into Danmakufu tutorial.
Title: Re: Another Danmakufu Tutorial?
Post by: Chronojet ⚙ Dragon on August 26, 2009, 05:52:26 AM
Quote
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.

Quote
#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.

...

How about this: I managed to do something like CtC does.

Code: [Select]
script_enemy cutin {
    let cutinimage = "script\img\cut_in.png";
    let frame = 0;
    let rotate = 0;
    @Initialize {
        SetLife(1);
        LoadGraphic(cutinimage);
        SetMovePosition02(GetClipMaxX-100, GetClipMaxY-100, 10);
    }

    @MainLoop {
        frame++;
        if(rotate >= 90) { VanishEnemy; }
        if(frame >= 51 && rotate < 90) { rotate += 15; }
        yield;
    }

    @DrawLoop {
        SetTexture(cutinimage);
        SetGraphicRect(2, 1710, 164, 1899);
        SetGraphicAngle(0, rotate, 0);
        DrawGraphic(GetX, GetY);
    }

    @Finalize {
        DeleteGraphic(cutinimage);
    }
}

Now all I have to do is put
Code: [Select]
CreateEnemyFromScript("cutin", GetClipMinX-100, GetClipMaxY-100, 0, 0, 0);in @Initialize of script_enemy_main and that's it.

 :yukkuri:

Actually, screw that. Helepolis just made a custom cut-in script and it looks way more awesome.