Show Posts

You can view here all posts made by this member. Note that you can only see posts made in areas to which you currently have access.


Topics - eri0o

Pages: [1] 2 3
1
was anyone ever able to recreate leaves animating in low resolution games (320x240 // 320x180) through code in success?

I am looking for an effect similar to the one in Owlboy.

Add spoiler tag for Hidden:


I looked over the Underwater module, because it's the most similar I could remember, but I don't understand yet how to modify until a flailing effect can be made.

Add spoiler tag for Hidden:
LeavesAnimation.asc
Code: Adventure Game Studio
  1. // new module script
  2. #define MAX_FRAMES 10
  3.  
  4. #define MAX_BLOCKS 128
  5.  
  6.  
  7. int block_w;
  8. int block_h;
  9. int block_x[MAX_BLOCKS];
  10. int block_y[MAX_BLOCKS];
  11.  
  12. bool Leaves_enabled = false;
  13. int original_graphic;
  14.  
  15. float tt = 0.0;
  16. float t = 0.0;
  17. float a[MAX_FRAMES], b[MAX_FRAMES], c[MAX_FRAMES];
  18.  
  19. DynamicSprite * dyn_spr;
  20.  
  21. static void LeavesAnimation::SetBaseGaphic(int leavesGraphic){
  22.   if(dyn_spr!=null){
  23.     dyn_spr.Delete();  
  24.   }
  25.   original_graphic = leavesGraphic;
  26.   dyn_spr = DynamicSprite.CreateFromExistingSprite(original_graphic, true);
  27. }
  28.  
  29. static int LeavesAnimation::GetGaphic(){
  30.   if(dyn_spr!=null){
  31.     return dyn_spr.Graphic;  
  32.   }
  33.   return 0;
  34. }
  35.  
  36. static void LeavesAnimation::Enable() {
  37.   float scalar = 8000.0;
  38.   int i = 0;
  39.  
  40.   int delta1 = Random(MAX_FRAMES-1);
  41.   int delta2 = Random(MAX_FRAMES-1);
  42.   int delta3 = Random(MAX_FRAMES-1);
  43.  
  44.   while (i < MAX_FRAMES) {
  45.     a[i] = Maths.Cos(Maths.Pi*IntToFloat((delta1+i)%MAX_FRAMES)/IntToFloat(MAX_FRAMES));  //IntToFloat( Random(2000)*(Random(1)-1) )/scalar;
  46.     b[i] = Maths.Sin(Maths.Pi*IntToFloat((delta2+i)%MAX_FRAMES)/IntToFloat(MAX_FRAMES));  //IntToFloat( Random(2000)*(Random(1)-1) )/scalar;
  47.     c[i] = Maths.Cos(Maths.Pi*IntToFloat((delta3+i)%MAX_FRAMES)/IntToFloat(MAX_FRAMES));  //IntToFloat( Random(2000)*(Random(1)-1) )/scalar;
  48.     i++;
  49.   }
  50.  
  51.   block_w = Game.SpriteWidth[original_graphic]/48;
  52.   block_h = Game.SpriteHeight[original_graphic]/24;
  53.  
  54.   i = 0;
  55.   while (i < MAX_BLOCKS) {
  56.     block_x[i] = Random(Game.SpriteWidth[original_graphic]- 1 - block_w);
  57.     block_y[i] = Random(Game.SpriteHeight[original_graphic] - 1 - block_h);
  58.     i++;
  59.   }
  60.   Leaves_enabled = true;
  61. }
  62.  
  63. static void LeavesAnimation::Disable() {
  64.   Leaves_enabled = false;
  65. }
  66.  
  67. float get_gradient_x(int xi, int yi) {
  68.   float x = IntToFloat(xi)/160.0 - 1.0;
  69.   float y = IntToFloat(yi)/160.0 - 1.0;
  70.  
  71.   float dx = 0.0;
  72.  
  73.   int i = 0;
  74.   while (i < MAX_FRAMES) {
  75.     dx += a[i]*t*Maths.Cos(a[i]*t*x + b[i]*t*y + c[i]);
  76.     i++;
  77.   }
  78.   return dx;
  79. }
  80.  
  81. float get_gradient_y(int xi, int yi) {
  82.   float x = IntToFloat(xi)/160.0 - 1.0;
  83.   float y = IntToFloat(yi)/160.0 - 1.0;
  84.  
  85.   float dy = 0.0;
  86.  
  87.   int i = 0;
  88.   while (i < MAX_FRAMES) {
  89.     dy += b[i]*t*Maths.Cos(a[i]*t*x + b[i]*t*y + c[i]);
  90.     i++;
  91.   }
  92.   return dy;
  93. }
  94.  
  95. function offset_a_block(DrawingSurface *surf, int x, int y, int w, int h) {
  96.   int xoff = FloatToInt(1.0*get_gradient_x(x + w/2, y + w/2));
  97.   int yoff = FloatToInt(1.0*get_gradient_y(x + h/2, y + h/2));
  98.  
  99.   xoff = Utils.clampInt(xoff, 1, -1);
  100.   yoff = Utils.clampInt(yoff, 1, 0);
  101.  
  102.   //w = Utils.clampInt(w, Game.SpriteWidth[original_graphic]-x, 1);
  103.   //w = Utils.clampInt(w, Game.SpriteHeight[original_graphic]-y, 1);
  104.  
  105.   DynamicSprite *ds = DynamicSprite.CreateFromDrawingSurface(surf, x, y, w, h);
  106.   //surf.DrawingColor = COLOR_TRANSPARENT;
  107.   //surf.DrawRectangle(x, y, x+w, y+w);
  108.   surf.DrawImage(x - xoff, y - yoff, ds.Graphic);
  109. }
  110.  
  111. function repeatedly_execute_always() {
  112.   if (Leaves_enabled) {
  113.     if(dyn_spr==null){
  114.       return;
  115.     }
  116.    
  117.    if(getCurrentFrame()%4!=0){
  118.       return;  
  119.     }
  120.    
  121.     tt = tt + 0.01;
  122.     float k = Utils.clampFloat((2.0*Maths.Pi*Maths.Sin(tt)), 8.0, 0.0) ; ///8.0;
  123.  
  124.     t=1.0;
  125.  
  126.     if(dyn_spr!=null){
  127.       dyn_spr.Delete();  
  128.     }
  129.     dyn_spr = DynamicSprite.CreateFromExistingSprite(original_graphic, true);
  130.  
  131.     //SetBackgroundFrame(0);
  132.     // repeatedly grab a random chunk from bg 1 and paste it to bg 0, slightly offset
  133.     DrawingSurface *surf = dyn_spr.GetDrawingSurface();  //Room.GetDrawingSurfaceForBackground(0);
  134.  
  135.     int i = 0;
  136.     while (i < MAX_BLOCKS) {
  137.       if(Random(FloatToInt(k))==0){
  138.         block_x[i] = Random(Game.SpriteWidth[original_graphic]- 1 - block_w);
  139.         block_y[i] = Random(Game.SpriteHeight[original_graphic] - 1 - block_h);
  140.       }
  141.       //int x = Random(Game.SpriteWidth[original_graphic]- 1 - block_w);
  142.       //int y = Random(Game.SpriteHeight[original_graphic] - 1 - block_h);
  143.       offset_a_block(surf, block_x[i], block_y[i], block_w, block_h);
  144.       i++;
  145.     }
  146.  
  147.     surf.Release();
  148.   }
  149. }
  150.  
  151.  
  152.     // draw some more around the player
  153.     // ViewFrame *frame = Game.GetViewFrame(player.View, player.Loop, player.Frame);
  154.     // int graphic = frame.Graphic;
  155.     // int height = FloatToInt(IntToFloat(Game.SpriteHeight[graphic]*player.Scaling)/100.0);
  156.     // int width  = FloatToInt(IntToFloat( Game.SpriteWidth[graphic]*player.Scaling)/100.0);
  157.     // int left = player.x - width/2;
  158.     // int top = player.y - height - player.z;
  159.  
  160.     // i = 0;
  161.     // while (i < 32) {
  162.     //   i++;
  163.     //   int w = Room.Width/64;
  164.     //   int x = left - w/2 + Random(width - 1);
  165.     //   int y = top - w/2 + Random(height - 1);
  166.     //   if (x < 0) x = 0;
  167.     //   else if (x >= Room.Width - w) x = Room.Width - 1 - w;
  168.  
  169.     //   if (y < 0) y = 0;
  170.     //   else if (y >= Room.Height - w) y = Room.Height - 1 - w;  
  171.  
  172.     //   offset_a_block(surf, x, y, w);
  173.     // }
  174.  

LeavesAnimation.ash
Code: Adventure Game Studio
  1. // new module header
  2.  
  3. struct LeavesAnimation {
  4.  
  5. import static void Enable();
  6. import static void Disable();
  7. import static void SetBaseGaphic(int leavesGraphic);
  8. import static int GetGaphic();
  9.  
  10. };
  11.  

Here is a different gif idea (but I still can't understand how to implement leaf animation on the bg...)
Add spoiler tag for Hidden:


2
Very weird question: sometimes I have to just sketch some weird math logic, and I build my game a thousand times just to read the result on a display function. Does an ags script interpreter for only the non graphical functions (maths and String I think) exists? A headless ags of sorts... Maybe AGS Script is based on something of same syntax...

3
Beginners' Technical Questions / A random game player
« on: 09 Jun 2018, 06:17 »
I was procrastinating researching the web and found the below post from Grumpy Gamer (misleading title). If you don't want to read it, go for the video.

https://grumpygamer.com/unit_testing_games

I liked the idea of something randomly playing a game until it crashes and logging the crash. Has someone here ever devised such a tool for an AGS game? (I was'n sure where to place this question...)

4
Does a Mouse.Click should trigger a on_mouse_click event? If they are in different modules, does order matter? Does Mouse.Click holds button down (for Mouse.isButtonDown) for a single tick?

5
I need to tint a character portrait - single frame speech view, and no limp sync and no blinking.

I have thought about, but I am not sure how to do it. :(

6
Hey!

I think since AGS 3.4.1 you can set the line spacing in fonts, over properties, inside AGS. It's awesome!

I just found a little bug, when editing a GUI in the editor (multiline label using [ character), the font LineSpacing is not considered for rendering the GUI in the Editor - but will be considered when the game runs in engine!

Just reporting for now.

7
Disclaimer: this an article that I sketched long ago, but didn't knew if it was good enough, so I am throwing here to see if ideas can come up to make it better because I want to address this general idea in my blog.

I noticed recently that during the week I am more keen on games that have story depth, but I can get tired if a game is too demanding skill wise. On weekends, I am usually well rested, and like to be challenged.

Once I saw a tweet by @gritfish that said:

Quote
"I honestly believe we need to  change the way games do difficulty / game modes to something like:

I'm here for the story
I'm here for challenge
I'm here for a second & harder playthrough
I'm here to take photos
I want to play with the settings and I'm okay if that breaks things
"

Well, I like this idea. But I am also slightly lazy, I am not coding different game modes.

I've read Designing Virtual Worlds, from Richard Bartle, and he talks about the 4 types of players he discovered while analyzing MUD: Achievers, who like to achieve defined goals; Socializers, who gets greatest rewards interacting with other people; Explorers, who likes to learn more about the virtual world; Killers, who like to dominate others. He goes on to explain how to create a virtual world that attracts each subset of players.

Players of Magic The Gathering may be familiar with this idea, it's R&D and Mark Rosewater divide players in three categories: Timmy, a social person who wants to have fun winning big (the RTS equivalent would be having a big base before unleashing a final gigantic attack); Johnny, who wants to express himself through the game; and Spike, who plays for the winning and competition. Recently they also started considering hybrids of these players.

On with adventure games. They are mostly known by stories, puzzles, the exploring and the pace on the control of the player. Psychographics can be defined as a quantitative methodology used to describe consumers on psychological attributes. Psychographics has been applied to the study of personality, values, opinions, attitudes, interests, and lifestyles.

I would like to propose here psychographics for Point and Click Adventure Games:

 - Stella, who plays for the Story;
 - Paula, who plays for the Puzzles;
 - Ezra, who plays to Explore;

So each time I create a room in a game, I try to look if I am leaving each of these players, something to play with, and also, I usually choose one of them to focus more on each room.

8
Hello,

I need to get the current graphic from the player, I need to do this every instant, I am using this information to draw things on a surface, so at instant t=7 I need the player frame for instant t=7, and not t=6.

I decided to use the following:

Code: Adventure Game Studio
  1. int getPlayerGraphic(){
  2.   ViewFrame * ViewFrame_Player;
  3.   ViewFrame_Player = Game.GetViewFrame(player.View,  player.Loop, player.Frame);
  4.   return  ViewFrame_Player.Graphic;
  5. }
  6.  

but apparently, this gets in the current instant, the graphic that was on screen on the previous instant, so using this information gets a slightly delay.

So I evolved to do the following thing:

Code: Adventure Game Studio
  1. int i_view;
  2. int i_loop;
  3. ViewFrame * ViewFrame_Player;
  4.  
  5. function repeatedly_execute_always(){
  6.   i_view = player.View;
  7.   i_loop = player.Loop;
  8. }
  9.  
  10. function late_repeatedly_execute_always(){
  11.   if(IsGamePaused() == 1  || !System.HasInputFocus){
  12.     return;  
  13.   }
  14.  
  15.   ViewFrame_Player = Game.GetViewFrame(i_view,  i_loop, player.Frame);
  16. }
  17.  
  18. static int PlayerViewframe::getGraphic(){
  19.   return  ViewFrame_Player.Graphic;
  20. }
  21.  

which sometimes works, but in some rare occasions, crashes my software, because player.Frame sometimes is updated and i_view and i_loop isn't, and my loop for down and up have different frame count than left and right. And sometimes, it doesn't update correctly.

So is there a right way of getting the current graphic the player has ?

9
Engine Development / Let's build for Android!
« on: 08 Jan 2018, 00:35 »
I will try to explain what I did here, questions will arise, hopefully everything can be answered here and this will allow me to generate a guide.

So first two things are important:
 - Android port of the engine currently doesn't report correctly mouse.isButtonDown, so your game CAN'T BE USING THIS, use if(System.OperatingSystem == eOSAndroid) in your code to work for this.
 - I am using the latest prebuilt APK as libs, those were for the previous stable version, so your game must be built with that AGS.

1. You need to install the latest Android Studio, just download and install. https://developer.android.com/studio/index.html .

2. You will also need JDK, NDK and Android SDK.

3. After installing Android Studio, download the repository I made here: https://github.com/ericoporto/mythsuntold_dungeonhands . This is my modded version of Monkey's Android Studio Project.

4. Open the folder in Android Studio.

5. Building should generate my game .apk!

6. Ok, let's make YOUR game .apk. In the folder Android Studio placed the Sdk, there is a magical tool called jobb. I use Ubuntu, and the tool is in the folder ~/Android/Sdk/tools/bin/ in my computer. If you use Windows, then it's probably in %appdata%\Local\Android\Sdk . (if someone can confirm this information, it would be awesome!). This is just so you can understand what will happen next.

7. In your game Compiled folder, there is a YOURGAMENAME.ags file. Open a cmd.exe (windows) or bash terminal (Linux/OSX). Change Directory to the folder YOURGAMENAME.ags is.

In Windows (untested!)
Code: Adventure Game Studio
  1. cd YOURGAMENAME\Compiled\
  2. mkdir obb
  3. move YOURGAMENAME.ags obb\
  4. %appdata%\Local\Android\Sdk\tools\bin\jobb -d \obb\ -o main.3.com.YOURSTUDIONAME.YOURGAMENAME.obb -pn com.YOURSTUDIONAME.YOURGAMENAME -pv 3
  5.  
   
In Linux/OSX
Code: Adventure Game Studio
  1. cd YOURGAMENAME/Compiled/
  2. mkdir obb
  3. mv YOURGAMENAME.ags obb/
  4. ~/Android/Sdk/tools/bin/jobb -d ./obb/ -o main.3.com.YOURSTUDIONAME.YOURGAMENAME.obb -pn com.YOURSTUDIONAME.YOURGAMENAME -pv 3
  5.  

In the code above, 3 is simply the OBB version number. Every time you change the content of the OBB linked to your APK and release, you have to increase that number - and update in your Android Studio Project accordingly. Also make sure that YOURGAMENAME and YOURSTUDIONAME are correctly updated in your android studio project.

8. In the README there is a line called Setting up the project for your game. I will copy below:

-Update package name:

Open the project in Android Studio, then in the project tree navigate to app/java/com.mythsuntold.osd.scourge.
Right-click on this folder and select "Refactor -> Move...". When prompted, select "Move package 'com.mythsuntold.dungeonhands' to another package" (the default). You may receive a warning that multiple directories will be moved, select Yes. Type the parent name of your package, not the final package name (e.g., com.bigbluecup not com.bigbluecup.game), select "Refactor" and then click "Do Refactor".
Right-click on the new project folder in the project tree (e.g., com.bigbluecup.scourge) and select "Refactor -> Rename". Type the package name for your game (e.g., game), then select "Refactor" and click "Do Refactor".

Finally, delete the com.mythsuntold.dungeonhands folder.

- Update project.properties. This file contains gradle settings related to your project. The application ID, version code, and version name need to be set to match your project settings (application ID is your package name).

- Update project.xml. This file contains resources for your project. The values there are described in that file.

- Update local.static.properties. This file contains local data that should NOT be added to version control (.gitignore will ignore your changes to this file). You need to add your keystore path, alias, and passwords, and optionally the path to your copy of the AGS source (if you are rebuilding the engine native libraries). See the Java docs on keytool or use the Android Studio signing wizard to generate a keystore.

- Update private.xml. This file contains definitions for your RSA public key and an integer-array with bytes for your salt which is used by the ExpansionDownloaderService. These values are necessary if distributing your app via the Google Play Store. The RSA public key is provided in the Google Play Developer Console, and the salt bytes may be any number of values in the range [-128, 127]. You may need to upload an APK without the RSA public key first before the key is provided. That APK should not be public unless the OBB file is embedded.

- Update graphics resources (app/src/main/res). You can use the Android Asset Studio to easily generate graphics for your app, or use your preferred method. Everything in the drawable and various mipmap-* folders should be replaced with your resources.

I made this post with the intent to help Mehrdad, so please try this!

10
Adventure Related Talk & Chat / Pixel Font Resource
« on: 03 Jan 2018, 21:30 »
Just linking a website that has been very useful to me:

http://www.pentacom.jp/pentacom/bitfontmaker2/gallery/?page=1&order=popular

11
Site & Forum Reports / Is teamcity still online?
« on: 30 Dec 2017, 15:07 »
There are some links that goes to Teamcity bigbluecup, it looks like it is/was a pipeline part to build binaries. Is this still online? Are the scripts for build there public in any place?

12
Hello,

I am making the opening scene of my game and I have a question. Playtesting showed that players prefer dialogs to be click to advance only instead of click + timer. In the opening scene though, I feel it would be better if the dialog used click to advance plus timer, because each looping state feel better under a time constraint.

Would keep my game click to advance and the opening click to advance + timer be a good or a bad idea?

13
I noticed the music position in ms is not updated every frame. Is this intentional?

I came up with the following code to fix this, where actualACMusMs has the true music position:

Code: Adventure Game Studio
  1. AudioChannel *acMusic;
  2.  
  3. //fix to report correct music position, game in 60fps
  4. int frame;
  5. int acMusPreviousMs;
  6. bool startedRepeat;
  7. int frameWhenRepeatingStarted;
  8. int actualACMusMs;
  9.  
  10. void playMusic(AudioClip * musicClip){
  11.   acMusic = musicClip.Play(eAudioPriorityHigh, eRepeat);
  12. }
  13.  
  14.  
  15. void repeatedly_execute_always(){
  16.   if(IsGamePaused()==1 || acMusic==null){
  17.     return;  
  18.   }
  19.  
  20.   frame++;
  21.  
  22.   if(acMusic.PositionMs==acMusPreviousMs){
  23.     if(!startedRepeat){
  24.       frameWhenRepeatingStarted=frame-1;
  25.     }
  26.     startedRepeat=true;
  27.     actualACMusMs = acMusPreviousMs+(frame-frameWhenRepeatingStarted)*16;
  28.   } else {
  29.     startedRepeat=false;
  30.     actualACMusMs= acMusic.PositionMs;
  31.   }
  32.  
  33.   acMusPreviousMs = acMusic.PositionMs;
  34. }
  35.  

I did this because I am experimenting with some music in my game that is active, like, I switch music depending on changes on the environment, and needed to correctly position the music when switching.

Also, imagine a graph:

(music 1)--->>(music 2)<< loop music 2 forever

How would you detect music 1 has ended and you need to proceed to music 2? Also if I have crossfade activated, how should I force crossfade to not happen then?

14
Advanced Technical Forum / Prevent alt+tab audio glitch
« on: 10 Dec 2017, 20:40 »
Hey! When I alt+tab an AGS game, the audio likely rolls a dice, and on a odd number, it glitches, producing looping sound that sounds like the previous 200ms of sound. Is there a way to prevent this? If I could detect from in game the loss of focus, I could try to lower the volume.

15
Hey, I have a code as below, my character cChara is correctly shown in screen. But SOMETIMES, I get an error on the line that has Game.GetViewFrame, stating Error: GetGameParameter: invalid frame specified.

Code: Adventure Game Studio
  1. ViewFrame * tvf;
  2.  
  3. ...
  4. function late_repeatedly_execute_always(){
  5.   ...
  6.   tvf = Game.GetViewFrame(cChara.View, cChara.Loop, cChara.Frame);
  7.   ...
  8. }
  9.  

The only thing I gathered is it happens when the character is walking to some direction and I click to force him walk down, and my walking down (and up) animation has one less frame then the walking right and left.

16
Advanced Technical Forum / Using Normal Maps (demo)
« on: 09 Dec 2017, 22:38 »
Ok, so today I lost some hours of my life coding NormalMap with AGS Script.

In case you want it, Download the Demo here!.

I don't think it looks good, I decided to ditch this idea, but maybe someone wants to try and test this.

I based my code on the code here.

If someone does make it better, faster, please share your enhancemets here!


17
Completed Game Announcements / Dungeon Hands
« on: 05 Dec 2017, 01:36 »
Dungeon Hands



Dungeon Hands is a two player card game you can play on a computer, made for LudumDare40. Click and drag to move cards.

Game Page on Itch.io
Ludum Dare 40 entry

Music and Graphics by Ricardo Juchem
Game Design by Kamila Galvani
Code and Stuff by √Črico Porto

Here are the rules

You win when you have no cards in hand.

When the game starts players can choose Monster Overlord or Heroes. The player with Monster Overlord deck starts.

A round is composed of each player turns.

On the start of the turn, the player can play any special card if desired. Then the player must play either a Hero card (when playing with Heroes) or a Monster card (if you are the Monster Overlord). Then the game goes to next turn.

The player playing next can play any special card if desired. Then the player must play either a Hero card (when playing with Heroes) or a Monster card (if you are the Monster Overlord), but the card played MUST be either a higher Rank or the same Class, this is called a valid card. If a player does not have a valid card in his hand, that player draws cards until a valid card is drawn and played. Then the turn ends.

With Monster Overlord cards and Heroes cards on the table, the dungeon encounter is resolved, and all cards on table leave play - the Hero cards go back to the village and the Monsters cards perish. The player who had the highest Rank card get to start the next round. If all cards had the same Rank, the last player to play starts the next round.

The Rank is the number on the corner, Class is represented by the symbol and color of the card. Special cards have no Rank or Class.

The player who draws the last deck card loses.

18
Hey guys,

Recently I started a thread on TIGSource with the name Game feel and juice for old school pixel point and click adventure game. If someone wants to know more about juice, I recommend the Juice it or Lose it presentation in video. So I thought to replicate the answers that came around here in the forums, to maybe try to get some specifics in AGS - for example, Chicky already told me to use 60FPS and using GUIs as overlays, maybe this also invites him to chat more here?

  • leave trace, give stuff the player can interact that leaves visible trace in the room.
  • break the rules where makes sense. The character should be able to look things from far away if he doesn't NEED to move there, saving the walking time.
  • extra flair on the rewarding: has the player picked a new item? Flash the screen, play a cool sound.
  • clarify when you reveal new text on the first time. If your game has repeated outcomes for the same input (looking something yields the same message), clearly tell the player the first time he sees, it's something new.
  • visual feedback for interation. This is a lot of work, requiring lots of animation, but feels good.
  • tweening. Tween EVERYTHING.
  • voice acting. This one is hard because I think games work a lot better with voice acting, but this is hard if you don't live on an English speaking country.

If anyone has any ideas to contribute, AGS specific things, please do. The tween everything item I think needs to be done throughout the whole development, but some things can wait the polishing phase. If some can share input on when it's interesting to think on these things would be cool too.

19
http://archive.is/FFx6z | https://www.polygon.com/2017/9/8/16263050/game-design-magic-tricks

Article: Game Design Magic Tricks

Hey, I read this article and was curious on which magic tricks could be used in point and click adventure games. Anyone has ideas?

20
Advanced Technical Forum / How to crossfade music
« on: 11 Nov 2017, 20:09 »
Hello!

I need to crossfade between two music tracks. I think this isn't so unusual, so probably someone here has already done it.

an excerpt of my audio player code is below
Code: Adventure Game Studio
  1. AudioChannel *acMusic;
  2. AudioClip * currentMusic;
  3. bool stopped;
  4. int globalMusicVol;
  5. AudioClip * previousmusic ;
  6.  
  7.  
  8. static void MusicPlayer::play(AudioClip * musicClip,  int position){
  9.   stopped = false;
  10.  
  11.   if(previousmusic==musicClip){
  12.     //don't play music if it's already playing
  13.     return;
  14.   }
  15.  
  16.   //stop previous music
  17.   if(previousmusic != null){
  18.     previousmusic.Stop();
  19.   }
  20.  
  21.   previousmusic = musicClip;
  22.  
  23.   currentMusic = musicClip;
  24.   if(position == 0){
  25.     acMusic = musicClip.Play(eAudioPriorityHigh, eRepeat);
  26.   } else {
  27.     acMusic = musicClip.PlayFrom(position,eAudioPriorityHigh, eRepeat);
  28.    
  29.   }
  30.   acMusic.Volume = globalMusicVol;
  31.   updateAllVolumes();
  32. }
  33.  
  34. static void MusicPlayer::playFromPreviousTime(AudioClip * musicClip, float crossfadeTime){
  35.   if(crossfadeTime!= 0.0){
  36.    
  37.   } else {
  38.     int previousPosition = acMusic.Position;
  39.     MusicPlayer.play(musicClip, previousPosition);
  40.   }
  41. }
  42.  

The function I am working is static void MusicPlayer::playFromPreviousTime(AudioClip * musicClip, float crossfadeTime).

I want to enable the possibility to crossfade between two songs that have the exact same length. How to do this? I only have 1 audio channel for music, and I am not using the ambient channel, would be possible to set the audioclip type to whichever isn't the last one?

Cassiebsg told me there is the possibility to use the following function too:
SetGameOption (OPT_CROSSFADEMUSIC, x); // where x= 0 (no), 1 (slow), 2 (slowish), 3 (medium) and 4 (fast)

But... I would like to have control over the crossfade time. Below is my full audio module code, it has dependency on the Tween Module.


MusicPlayer.ash
Add spoiler tag for Hidden:
Code: Adventure Game Studio
  1. // new module header
  2.  
  3. #define MusicPlayer_CROSSFADETIME 0
  4.  
  5.  
  6. struct MusicPlayer
  7. {
  8.   import static void play(AudioClip * musicClip,  int position = 0);
  9.   import static void playFromPreviousTime(AudioClip * musicClip, float crossfadeTime = MusicPlayer_CROSSFADETIME);
  10.   import static AudioClip * getCurrentMusic();
  11.   import static void stop(AudioClip * musicClip);
  12.   import static void setMusicPosition(int x, int y);
  13.   import static void removeMusicPosition();
  14.   import static void tweenMusicPosition(int x, int y,  float timing);
  15.   import static void setGlobalVolume(int musicVolume);
  16.   import static int  getGlobalVolume();
  17.   import static void setVolume(int musicVolume);
  18.   import static void tweenVolume(int musicVolume, float timing);
  19. };
  20.  
  21. struct SFXPlayer
  22. {
  23.   import static void play(AudioClip * soundClip);
  24.   import static void stop(AudioClip * soundClip);
  25.   import static void setGlobalVolume(int soundVolume);
  26.   import static int  getGlobalVolume();
  27.   import static void setVolume(int soundVolume);
  28. };
  29.  
  30. import void SPlayerSetGameVolume(int gameVolume);
  31. import int SPlayerGetGameVolume();
  32. import void SPlayerSetDefaults();
  33.  
  34.  

MusicPlayer.asc
Add spoiler tag for Hidden:
Code: Adventure Game Studio
  1. // MusicPlayer variables
  2. AudioChannel *acMusic;
  3. AudioClip * currentMusic;
  4. bool stopped;
  5. int globalMusicVol;
  6. int currentMusicVolue;
  7.  
  8. // SFXPlayer variables
  9. AudioChannel *acSound;
  10. int gloabalSoundVol;
  11. int orignalMusicX;
  12. int orignalMusicY;
  13.  
  14. function updateAllVolumes(){
  15.   int i = 0;
  16.   AudioChannel *ac;
  17.    
  18.   while (i < System.AudioChannelCount)
  19.   {
  20.     ac = System.AudioChannels[i];
  21.    
  22.     if (ac.PlayingClip != currentMusic)
  23.     {
  24.       ac.Volume = gloabalSoundVol;
  25.     } else {
  26.       ac.Volume = globalMusicVol;
  27.     }
  28.     i++;
  29.   }  
  30. }
  31.  
  32.  
  33. AudioClip * previousmusic ;
  34.  
  35.  
  36. static AudioClip * MusicPlayer::getCurrentMusic(){
  37.   return currentMusic;  
  38. }
  39.  
  40. static void MusicPlayer::play(AudioClip * musicClip,  int position){
  41.   stopped = false;
  42.  
  43.   if(previousmusic==musicClip){
  44.     //don't play music if it's already playing
  45.     return;
  46.   }
  47.  
  48.   //stop previous music
  49.   if(previousmusic != null){
  50.     previousmusic.Stop();
  51.   }
  52.  
  53.   previousmusic = musicClip;
  54.  
  55.   currentMusic = musicClip;
  56.   if(position == 0){
  57.     acMusic = musicClip.Play(eAudioPriorityHigh, eRepeat);
  58.   } else {
  59.     acMusic = musicClip.PlayFrom(position,eAudioPriorityHigh, eRepeat);
  60.    
  61.   }
  62.   acMusic.Volume = globalMusicVol;
  63.   updateAllVolumes();
  64. }
  65.  
  66.  
  67. static void MusicPlayer::playFromPreviousTime(AudioClip * musicClip, float crossfadeTime){
  68.   if(crossfadeTime!= 0.0){
  69.    
  70.   } else {
  71.     int previousPosition = acMusic.Position;
  72.     MusicPlayer.play(musicClip, previousPosition);
  73.   }
  74. }
  75.  
  76. static void MusicPlayer::setMusicPosition(int x, int y){
  77.   acMusic.SetRoomLocation(x, y);
  78.   orignalMusicX = x;
  79.   orignalMusicY = y;
  80. }
  81.  
  82.  
  83. static void MusicPlayer::removeMusicPosition(){
  84.   acMusic.SetRoomLocation(0, 0);
  85.   orignalMusicX = 0;
  86.   orignalMusicY = 0;
  87. }
  88.  
  89. static void MusicPlayer::tweenMusicPosition(int x, int y,  float timing){
  90.   if(x==0 && y==0){
  91.     MusicPlayer.removeMusicPosition();
  92.   }
  93.  
  94.   if(orignalMusicX ==0 && orignalMusicY==0){
  95.     return;  
  96.   }
  97.  
  98.   acMusic.TweenRoomLocation(timing, x, y, orignalMusicX, orignalMusicY);
  99.   orignalMusicX = x;
  100.   orignalMusicY = y;
  101. }
  102.  
  103.  
  104. static void MusicPlayer::stop(AudioClip * musicClip){
  105.   stopped = true;
  106.   previousmusic = null;
  107.   musicClip.Stop();  
  108. }
  109.  
  110. static void MusicPlayer::setVolume(int musicVolume){
  111.   if(acMusic != null){
  112.     currentMusicVolue = musicVolume;
  113.     acMusic.Volume = FloatToInt(IntToFloat(currentMusicVolue) * IntToFloat(globalMusicVol)/100.0);
  114.   }
  115. }
  116.  
  117. static void MusicPlayer::tweenVolume(int musicVolume, float timing){
  118.   if(acMusic != null){
  119.     currentMusicVolue = musicVolume;
  120.     acMusic.TweenVolume(timing, FloatToInt(IntToFloat(musicVolume) * IntToFloat(globalMusicVol)/100.0), eEaseLinearTween, eNoBlockTween);
  121.   }
  122. }
  123.  
  124. static void MusicPlayer::setGlobalVolume(int musicVolume){
  125.   globalMusicVol = musicVolume;
  126.   Game.SetAudioTypeVolume(eAudioTypeAmbientSound, globalMusicVol, eVolExistingAndFuture);
  127.   Game.SetAudioTypeVolume(eAudioTypeMusic, globalMusicVol, eVolExistingAndFuture);
  128. }
  129.  
  130. static int MusicPlayer::getGlobalVolume(){
  131.   return globalMusicVol;  
  132. }
  133.  
  134.  
  135.  
  136. static void SFXPlayer::play(AudioClip * soundClip){
  137.   acSound = soundClip.Play(eAudioPriorityNormal, eOnce);
  138.   acSound.Volume = gloabalSoundVol;
  139.   updateAllVolumes();
  140. }
  141.  
  142. static void SFXPlayer::stop(AudioClip * soundClip){
  143.   soundClip.Stop();  
  144. }
  145.  
  146. static void SFXPlayer::setGlobalVolume(int soundVolume){
  147.   gloabalSoundVol = soundVolume;
  148.   Game.SetAudioTypeVolume(eAudioTypeSound, gloabalSoundVol, eVolExistingAndFuture);
  149. }
  150.  
  151. static void SFXPlayer::setVolume(int soundVolume){
  152.   if(acSound != null){
  153.     acSound.Volume = FloatToInt(IntToFloat(soundVolume) * IntToFloat(gloabalSoundVol)/100.0);
  154.   }
  155. }
  156.  
  157. static int SFXPlayer::getGlobalVolume(){
  158.   return gloabalSoundVol;  
  159. }
  160.  
  161. void  SPlayerSetGameVolume(int gameVolume){
  162.   System.Volume = gameVolume;
  163. }
  164.  
  165. int SPlayerGetGameVolume(){
  166.   return System.Volume;
  167. }
  168.  
  169. void SPlayerSetDefaults(){
  170.   MusicPlayer.setGlobalVolume(70);
  171.   SFXPlayer.setGlobalVolume(70);
  172.   SPlayerSetGameVolume(70);
  173. }
  174.  
  175. function game_start()
  176. {
  177.   SPlayerSetDefaults();
  178.   sldAudio.Value = System.Volume;
  179.   sldTestMainVolume.Value = System.Volume;
  180.   sldTestMusicVolume.Value = globalMusicVol;
  181.   sldTestSoundVolume.Value = gloabalSoundVol;
  182. }
  183.  

Pages: [1] 2 3