GDevelopApp / GDevelop-extensions

Repository of behaviors, actions, conditions and expressions to be used in GDevelop for creating games
https://gdevelop.io
MIT License
131 stars 53 forks source link

Camera Shake 2.x (proposed replacement of Camera Shake 1.0.0) #75

Closed tristanbob closed 3 years ago

tristanbob commented 3 years ago

Describe the extension

Shake the camera on the specified layer (ideal for explosions, hit/impacts, earthquake, etc.)

Choose which style of shake you want: (can use multiple) UsePosition = Shake the X and Y position of the camera (default=yes) UseAngle = Shake the rotation of the camera (default=no) UseZoom = Shake the zoom level of the camera (default=no)

Other Parameters: PowerX = Amplitude of the shaking on the X axis (default=5) PowerY = Amplitude of the shaking on the Y axis (default=5) Layer = (base layer if empty) Camera = (default=0) Duration (in seconds) (default=0.25)

Notes:

Checklist

Extension file

CameraShake-2.1.1.json.zip

4ian commented 3 years ago

Thanks for, as usual, a very nice extension :)

UseZoom does NOT look good; it looks like the rendering can't keep up with the changes. Is there any way to improve this?

Would you have an example demonstrating this? I'm not sure I was able to reproduce this.

4ian commented 3 years ago

A few comments:

Rather than use position/use angle/use zoom, we could have a X/Y amplitude (what you already have) (if set to 0, no shaking), a angle amplitude (in degrees) and a zoom amplitude? So that to disable something, you just leave it to 0? Let me know what you think.

tristanbob commented 3 years ago

Great question! Currently, I just used the PowerX value, but I want to find a better way to present that option to the user. I think I'll use your suggested idea, and get rid of all the booleans.

X movement amplitude "Default: 5" Y movement amplitude "Default: 5" Angle rotation amplitude "Default: 0" Zoom amplitude "Default: 0"

4ian commented 3 years ago

Sounds good ๐Ÿ‘

tristanbob commented 3 years ago

Here is a short video of me testing Zoom Shake. The right side of the screen doesn't finish rendering. I think this might be happening on all "zoom in" changes. https://discordapp.com/channels/258623956158906368/614526835807617044/787062948060332122

BTW, I am also adding the ability to set the frequency of oscillation for Zoom Shake and Angle Shake. I tried it with Position Shake, but I realized that it looks horrible if the camera is following an object... which is VERY common.

4ian commented 3 years ago

The right side of the screen doesn't finish rendering. I think this might be happening on all "zoom in" changes.

Verify closely, it's not a rendering problem because the objects of the base layer are shown. It's the background that is not resized properly ;) I.e: a bug in the Parallax extension for tiled sprite. I've fixed it! To check the fix, you can reinstall the "Parallax" extension in the Platformer object.

Check your extension with this and let me know

tristanbob commented 3 years ago

I just checked and you fixed it! I am very pleased with how zoom shake works now. Thanks for finding and fixing this bug.

FYI - I expect to have this extension submitted in a few hours.

tristanbob commented 3 years ago

Updated to 2.1.5. Changes include:

Let me know if you find any problems!

CameraShake-2.1.5.json.zip

4ian commented 3 years ago

Great, I'll take a look tomorrow ๐Ÿ‘ Thanks!

4ian commented 3 years ago

Thanks for the update :)

I have a few questions related to the slowdown. I'm not sure they have a unit currently. Usually a slowdown is a "speed": its unit is "something per second". For example, you would slowdown the shaking amplitude, initially at 20px, of 5 pixels per second (so that it's 15 after 1 second, 10 after 2 seconds, etc...). Same for amplitude (degrees per second) and the zoom (unit per second).

In your case I'm not sure exactly how this formula work: image

Shouldn't this be something like (in the example of the X position): initial position + amplitude * RandomFloatInRange(-1, 1)

RandomFloatInRange wil be give a result between -1 and 1, so the randomness will be distributed evenly

At every frame, the amplitude would be decreased by amplitude slowdown speed * TimeDelta().

I'm also not sure what you convey in these conditions: image

Note that in addition to the "speed of the slowdown" (which is in "unit per seconds") and the "amplitude" (which is in "units"), there is a third notion: how frequently do you change the the position (or the angle/zoom). Currently it seems to be at every frame, so 60 times per second (so a frequency of 60hz) - though I'm not sure because of the mod conditions. This is fine to keep this, but you can also run a timer to update these only every X seconds. If you want a frequency, just add a frequency parameter (for example: 30hz or 60hz by default), take the inverse (1/60=0.016) and this will be the time to wait before updating the position/angle/zoom.

Let me know if it makes sense. It's important to get all of these right because otherwise this can create surprising results or a dependency on the number of images per second :)

tristanbob commented 3 years ago

Thanks for taking the time to look at this so closely!

I have a few questions related to the slowdown. I'm not sure they have a unit currently. Usually a slowdown is a "speed": its unit is "something per second". For example, you would slowdown the shaking amplitude, initially at 20px, of 5 pixels per second (so that it's 15 after 1 second, 10 after 2 seconds, etc...).

Slowdown may not be the best word; what slowdown actually describes is how many frames should pass before changing the value again (position, angle, zoom). I thought about calling it "FrameSkip" but I didn't think it was obvious what it does.

What you are describing is how fast does the amplitude of the shake decay. My code uses a static linear decay, which is based on the percentage of timer that is left. (If timer has 25% left before reaching "duration", then set amplitude = 25% ). I thought about offering some exponential decay options (which would not be hard to add) but I'm not sure how much value that would add.

Shouldn't this be something like (in the example of the X position): initial position + amplitude * RandomFloatInRange(-1, 1)

RandomFloatInRange will be give a result between -1 and 1, so the randomness will be distributed evenly

For position shake, I use "RandomWithStep(-1,1,2)" to pick a random direction (positive or negative) for each state change.

For rotation and zoom shake, I thought they looked better when oscillating consistently from positive to negative for each state change. That is why they start with the initial value and alternate between adding and subtracting the shake values.

I'm also not sure what you convey in these conditions: image

I use the mod function with FrameCount to implement my slowdowns (frameskip) between state changes. I use two conditions to make sure I can alternate between adding and subtracting the shake value.

Note that in addition to the "speed of the slowdown" (which is in "unit per seconds") and the "amplitude" (which is in "units"), there is a third notion: how frequently do you change the the position (or the angle/zoom). Currently it seems to be at every frame, so 60 times per second (so a frequency of 60hz) - though I'm not sure because of the mod conditions. This is fine to keep this, but you can also run a timer to update these only every X seconds. If you want a frequency, just add a frequency parameter (for example: 30hz or 60hz by default), take the inverse (1/60=0.016) and this will be the time to wait before updating the position/angle/zoom.

What you call "speed of the slowdown" refers to the linear decay I described above. I do not currently offer any way for the user to change this.

What you call "how frequently do you change" is what my "slowdown" values control. The "slowdown" number entered by the user is how many frames should pass between state changes.

Do my explanations make sense? What changes would you like to see?

tristanbob commented 3 years ago

Instead of counting frames and using that to control the frequency of shaking, I think I might try using timers like you suggested.

Then I could change the current "slowdown" variables to "ShakeDelay" or "TimeBetweenShakes".

4ian commented 3 years ago

Overall I think "slowdown" is not the right word, it's like a frequency (frequency = "how many times you run something (here, a change) on a given period") (usually, this period of time is 1 second). TimeBetweenShakes would work, it's actually the inverse of a frequency (TimeBetweenShakes = 1/Frequency).

What you call "speed of the slowdown" refers to the linear decay I described above. I do not currently offer any way for the user to change this.

Linear decay is fine :) Doing something else would be very complex and not worth it. So let's keep the linear decay but have the "slowdown" renamed to "frequency" or "time between shakes" (though a "shake" could be seen as a series of changes... so frequency seems really the right word.). Using a timer or a variable should work for this! :) Thanks!

tristanbob commented 3 years ago

I am making good progress on changing from frame counting to timers. Do you think I should make timers for each type of shake, or just one global shake frequency timer? More would provide flexibility for users, but I'm also worried about making too many knobs to control. :)

4ian commented 3 years ago

I'm also worried about the "two many knobs" syndrome :) This would probably not be super useful, so let's keep a single frequency for all the updates. With each amplitude, and the time of the shake, there is already enough control on the shaking I think! :)

tristanbob commented 3 years ago

Here is the updated version. Changes include:

CameraShake-2.2.0.json.zip

4ian commented 3 years ago

Awesome, I'll check that tomorrow ๐Ÿ‘

tristanbob commented 3 years ago

FYI - I am now working on a object behavior that should provide the exact same shake options for an individual object (position, angle, scale).

I also hope to make some videos specifically showcasing each the extensions I've made, in case you want to include links to them.

tristanbob commented 3 years ago

Don't import 2.2.0. I discovered some issues on my ObjectShake that I want to fix in CameraShake. Thanks!

4ian commented 3 years ago

Alright I'm holding this then :)

tristanbob commented 3 years ago

Updated to 2.3.0. Changes include:

CameraShake-2.3.0.json.zip

tristanbob commented 3 years ago

I just realized that I can easily animate every frame on the way to a shake value. Hold off again, please. :)

tristanbob commented 3 years ago

This is now updated with the same improvements I added to ShakeObject (they use almost the exact same code):

CameraShake-2.4.0.json.zip

4ian commented 3 years ago

I think it's mostly good, with three modifications:

tristanbob commented 3 years ago

I'm going to push back (a little) on the change to Frequency, for two reasons:

1) Frequency changes are not a linear amount, which is not intuitive to users.

2) Frequency does not work well for slower shakes. I envision some use-cases where someone would want a slow pulsing of scale (a heartbeat effect) or gentle rocking of angle (a ship on the ocean) that take more than one second per shake.

I do agree and appreciate your other suggestions and will start working on them. :)

4ian commented 3 years ago

Frequency changes are not a linear amount, which is not intuitive to users.

On the other side, it's usually what we use for animations (60fps => fast, 10fps => slower) and for "fast moving" stuff, which is usually what I think of when we say "shake". This being said:

Frequency does not work well for slower shakes. I envision some use-cases where someone would want a slow pulsing of scale (a heartbeat effect) or gentle rocking of angle (a ship on the ocean) that take more than one second per shake.

That's fair enough, as it work for longer periods (in which case the displacement is done linearly). Let's keep this in seconds then!

Last thing: is it really a good idea to consider -1 as the value for nothing? I got very surprised when I put 0 for the amplitude on Y axis and it still moved... I know it's written but still, even me that reviewed the extension, I forgot about this. Should we just remove the handling of -1, meaning that 0 can be used as the value for disabling something. We can still in the long description put something like: "For example: 5". Let me know :)

tristanbob commented 3 years ago

I wish there was a way to know if the user entered "0" in a parameter, versus an empty variable that uses the default value of "0".

How are other people doing this? What is the best practice?

I had a goal that when someone used my extension without entering any values, they would get a decent outcome. However, I totally agree that when a user enters "0", it should be treated as "0". Unless you have some magic way to determine if a user has entered a value on a variable, I will change to "For example: 5"

tristanbob commented 3 years ago

This update includes the changes mentioned above:

CameraShake-2.4.1.json.zip

4ian commented 3 years ago

I wish there was a way to know if the user entered "0" in a parameter, versus an empty variable that uses the default value of "0". How are other people doing this? What is the best practice?

That's a good question. Internally in GDevelop there is a way to specify a default value that should be used when no value is entered. So you would be able to say "this parameter should be 1 if empty" (or any other value). This being said, it's not used a lot because it's not very explicit (a good first step would be to show a placeholder with the default value) and it's not well supported in the events sheet (nothing is shown, while we should show also a placeholder with the default value). In the case of an extension that would need a lot of default values or configuration, I suggest at this point to switch to another pattern, which is to have actions to "setup" the thing, and an action to launch it (with the mandatory parameters).

For example, this could be something like:

Conditions: ...
Actions:
- Set amplitude of the next shaking on X axis to 5px
- Set rotation amplitude of the next shaking to 40 degrees
- Set easing of the shaking to "quadratic"
- Start the shaking for 5 seconds

First actions when called are changing a (set of) variable(s), and only the last one is "launching" the shaking. This means that you're not forced to use all the actions, and if you don't then your extension should use default values :) Note that this is a useful pattern if you want to add things in the future - without losing compatibility with the existing games using the existing actions.

It's also making things a bit more complex to use - so usually this would be only for "complex" things like sending a network query.

4ian commented 3 years ago

I've merged the latest version of the extension. Great job on this! ๐Ÿ‘๐Ÿ‘

tristanbob commented 3 years ago

I created an example game to let people see the effects of different parameters. While making this, I realized that people might want some helper functions, so I added:

CameraShake-2.4.2.json.zip

https://victrisgames.itch.io/gdevelop-camera-shake-example

https://user-images.githubusercontent.com/8879811/103106489-3f854480-45f3-11eb-8772-0220d12d7f0e.mp4

4ian commented 3 years ago

Very nice! I've updated the extension (with minor fixes in the description/sentences - all are in the affirmative tense). I've also added a link in the description to your example, as it's really well made and show well what can be done with the extension :)

tristanbob commented 3 years ago

I created a short video that walks through the different ways that CameraShake can be used:

https://www.youtube.com/watch?v=Y4RlCuyaY0o