MODULE: rellax 0.4.0

Started by eri0o, Mon 07/10/2019 03:26:58

Previous topic - Next topic

eri0o

@Flugeldufel Thanks, I had never noticed that issue.

It happens if you get to a lower than 12 distance between Current Camera Position and the Wanted Camera Position apparently.

For now you can replace the rellax.asc file with the content below.

rellax.asc

Spoiler
Code: ags
// Rellax
// 0.1.5~
// A module to provide smooth scrolling and parallax!
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Before starting, you must create the following Custom Properties
// in AGS Editor, for usage with Objects.
// Just click on Properties [...] and on the Edit Custom Properties screen,
// click on Edit Schema ... button, and add the two properties below:
//
// PxPos:
//    Name: PxPos
//    Description: Object's horizontal parallax
//    Type: Number
//    Default Value: 0
//
// PyPos:
//    Name: PyPos
//    Description: Object's vertical parallax
//    Type: Number
//    Default Value: 0
//
//  The number defined on Px or Py will be divided by 100 and used to increase
// the scrolling. An object with Px and Py 0 is scrolled normally, an object
// with Px and Py 100 will be fixed on the screen despite camera movement.
//
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//
// based on Smooth Scrolling + Parallax Module
// by Alasdair Beckett, based on code by Steve McCrea.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

#define MAX_PARALLAX_OBJS 39

Character *_TargetCharacter;

Object *_pxo[MAX_PARALLAX_OBJS];
int _pxoRoomStartX[MAX_PARALLAX_OBJS];
int _pxoRoomStartY[MAX_PARALLAX_OBJS];
int _pxoOriginX[MAX_PARALLAX_OBJS];
int _pxoOriginY[MAX_PARALLAX_OBJS];
int _pxo_count;

int _cam_window_w, _cam_window_h;
float _scroll_x,  _scroll_y;
int _next_cam_x, _next_cam_y;
int _prev_c_x, _prev_c_y;
int _off_x, _off_y;
int _look_ahead_x = 48;
int _look_ahead_y = 16;

int _partial_c_height;
int _count_still_ticks;

bool _is_doRoomSetup;
bool _SmoothCamEnabled = true;
bool _ParallaxEnabled = true;

float _Abs(float v) {
  if(v<0.0) return -v;
  return v;
}

int _Lerp(float from, float to, float t) {
  return FloatToInt(from + (to - from) * t, eRoundNearest);
}

int _ClampInt(int value, int min, int max) {
  if (value > max) return max;
  else if (value < min) return min;
  return value;
}

void doObjectParallax(){
  int camx = _next_cam_x;
  int camy = _next_cam_y;

  for(int i=0; i<_pxo_count; i++){
    if(_pxo[i].GetProperty("PxPos") !=0 || _pxo[i].GetProperty("PyPos") != 0) {
      float parallax_x = IntToFloat(_pxo[i].GetProperty("PxPos"))/100.0;
      float parallax_y = IntToFloat(_pxo[i].GetProperty("PyPos"))/100.0;

      _pxo[i].X=_pxoOriginX[i]+FloatToInt(IntToFloat(camx)*parallax_x);
      _pxo[i].Y=_pxoOriginY[i]+FloatToInt(IntToFloat(camy)*parallax_y);
    }
  }
}

void _enable_parallax(bool enable) { 
  _ParallaxEnabled = enable;
}

void _enable_smoothcam(bool enable) {
  if(enable == true){
    doObjectParallax();
  }

  _SmoothCamEnabled = enable;  
}

void _set_targetcharacter(Character* target) {
  _TargetCharacter = target;
}

// ---- Rellax API ------------------------------------------------------------

void set_TargetCharacter(this Rellax*, Character* target)
{
  _set_targetcharacter(target);
}

Character* get_TargetCharacter(this Rellax*)
{
  return  _TargetCharacter;
}

void set_EnableParallax(this Rellax*, bool enable)
{ 
  _enable_parallax(enable);
}

bool get_EnableParallax(this Rellax*)
{
  return _ParallaxEnabled;
}

void set_EnableSmoothCam(this Rellax*, bool enable)
{ 
  _enable_smoothcam(enable);
}

bool get_EnableSmoothCam(this Rellax*)
{
  return _SmoothCamEnabled;
}

void set_CameraOffsetX(this Rellax*, int offset_x)
{ 
  _off_x = offset_x;
}

int get_CameraOffsetX(this Rellax*)
{
  return _off_x;
}

void set_CameraOffsetY(this Rellax*, int offset_y)
{ 
  _off_y = offset_y;
}

int get_CameraOffsetY(this Rellax*)
{
  return _off_y;
}

void set_CameraLookAheadX(this Rellax*, int look_ahead_x)
{ 
  _look_ahead_x = look_ahead_x;
}

int get_CameraLookAheadX(this Rellax*)
{
  return _look_ahead_x;
}

void set_CameraLookAheadY(this Rellax*, int look_ahead_y)
{ 
  _look_ahead_y = look_ahead_y;
}

int get_CameraLookAheadY(this Rellax*)
{
  return _look_ahead_y;
}

// ----------------------------------------------------------------------------

void doSetOrigins (){
  _pxo_count=0; // Reset the total number of parallax objects to zero
  float cam_w = IntToFloat(Game.Camera.Width);
  float cam_h = IntToFloat(Game.Camera.Height);
  float room_w = IntToFloat(Room.Width);
  float room_h = IntToFloat(Room.Height);

  for(int i=0; i<Room.ObjectCount; i++){
    if (object[i].GetProperty("PxPos")!=0) {
 			_pxo[_pxo_count]=object[i];
      float parallax_x = IntToFloat(object[i].GetProperty("PxPos"))/100.0;
      float parallax_y = IntToFloat(object[i].GetProperty("PyPos"))/100.0;

      float obj_x = IntToFloat(object[i].X);
      float obj_y = IntToFloat(object[i].Y);

      // initial positions for reset
      _pxoRoomStartX[_pxo_count]= object[i].X;
      _pxoRoomStartY[_pxo_count]= object[i].Y;

      //Set origin for object:
      _pxoOriginX[_pxo_count] = object[i].X -FloatToInt(
        parallax_x*obj_x*(room_w-cam_w) / room_w );

      _pxoOriginY[_pxo_count] = object[i].Y -FloatToInt(
        parallax_y*obj_y*(room_h-cam_h) / room_h );

			if(_pxo_count<MAX_PARALLAX_OBJS) _pxo_count++;
		}
   }
  doObjectParallax();
}

void doRoomSetup(){
  Game.Camera.X = _ClampInt(_TargetCharacter.x-Game.Camera.Width/2, 
    0, Room.Width-Game.Camera.Width);

  Game.Camera.Y = _ClampInt(_TargetCharacter.y-Game.Camera.Height/2, 
    0, Room.Height-Game.Camera.Height);

  _next_cam_x = Game.Camera.X;
  _next_cam_y = Game.Camera.Y;
  doSetOrigins();

  ViewFrame* c_vf = Game.GetViewFrame(_TargetCharacter.NormalView, 0, 0);
  float scaling = IntToFloat(GetScalingAt(_TargetCharacter.x, _TargetCharacter.y))/100.00;
  _partial_c_height = FloatToInt((IntToFloat(Game.SpriteHeight[c_vf.Graphic])*scaling)/3.0);

  if (_ParallaxEnabled) _enable_parallax(true);
  else _enable_parallax(false);
  _is_doRoomSetup = true;
}

void doSmoothCameraTracking(){
  if(_prev_c_x == _TargetCharacter.x && _prev_c_y == _TargetCharacter.y)
    _count_still_ticks++;
  else
    _count_still_ticks = 0;

  if(_TargetCharacter.x-Game.Camera.Width/2-Game.Camera.X<=-_cam_window_w/2 ||
     _TargetCharacter.x-Game.Camera.Width/2-Game.Camera.X>_cam_window_w/2 ||
     _TargetCharacter.y-Game.Camera.Height/2-Game.Camera.Y<=-_cam_window_h/2 ||
     _TargetCharacter.y-Game.Camera.Height/2-Game.Camera.Y>_cam_window_h/2 ||
     _count_still_ticks > 5){

    int x_focus=0,y_focus=0;
    if(_TargetCharacter.Loop == 2) x_focus = _look_ahead_x; // right
    else if(_TargetCharacter.Loop == 1) x_focus = -_look_ahead_x; // left
    else if(_TargetCharacter.Loop == 0) y_focus = _look_ahead_y; // down
    else if(_TargetCharacter.Loop == 3) y_focus = -_look_ahead_y; // up

    float y_multiplier;
    if(_count_still_ticks<=30) y_multiplier = IntToFloat(_count_still_ticks)*0.138 ;
    if(_count_still_ticks>30) y_multiplier = 5.0;

    float target_cam_x = IntToFloat(_ClampInt(_TargetCharacter.x + _off_x + x_focus - Game.Camera.Width/2,   0, Room.Width-Game.Camera.Width));
    float target_cam_y = IntToFloat(_ClampInt(_TargetCharacter.y + _off_y + y_focus - _partial_c_height - Game.Camera.Height/2,   0, Room.Height-Game.Camera.Height));
    float current_cam_x =  IntToFloat(Game.Camera.X);
    float current_cam_y =  IntToFloat(Game.Camera.Y);
    float lerp_factor_x = 0.04;
    float cam_distance_x = _Abs(target_cam_x - current_cam_x);
    
    if( cam_distance_x < 32.0 && cam_distance_x >= 1.0) {
      lerp_factor_x  = 0.913*Maths.RaiseToPower(cam_distance_x, -0.7268);
    }    

    _next_cam_x = _Lerp( current_cam_x, target_cam_x, lerp_factor_x );
    _next_cam_y = _Lerp( current_cam_y, target_cam_y, 0.01*y_multiplier);
  }

  _prev_c_x = _TargetCharacter.x;
  _prev_c_y = _TargetCharacter.y;
}

// --- callbacks --------------------------------------------------------------

function on_event (EventType event, int data){
  // player exits any room
  if (event==eEventLeaveRoom){
    for(int i=0; i<_pxo_count; i++){
      _pxo[i].X=_pxoRoomStartX[i];
      _pxo[i].Y=_pxoRoomStartY[i];
    }
    _is_doRoomSetup = false;
  }

  // player enters a room that's different from current
	if (event==eEventEnterRoomBeforeFadein){
    if(!_is_doRoomSetup){
      doRoomSetup();
    }
  }
}

function game_start(){
  
  System.VSync = true;
  _cam_window_w = 40;
  _cam_window_h = 40;
  _set_targetcharacter(player);
  _enable_parallax(true);
  _enable_smoothcam(true);
}

function late_repeatedly_execute_always(){
  if(_SmoothCamEnabled) doSmoothCameraTracking();
  if(_ParallaxEnabled) doObjectParallax();
  if(_SmoothCamEnabled) Game.Camera.SetAt(_next_cam_x, _next_cam_y);
  else {
    _next_cam_x = Game.Camera.X;
    _next_cam_y = Game.Camera.Y;
  }
}

function repeatedly_execute_always(){
  if(!_is_doRoomSetup) doRoomSetup();
}
[close]

This should fix for now, but I am not completely satisfied with the resulting camera movement, kinda wanted to be a bit smoother, so I need to think a little bit more. Please confirm that the above code temporarily solves for you.

Flugeldufel

Oh thank you for your quick response  :)
I‘m currently on the phone, but I will test it within the next hours, as soon as I‘m at home.

Flugeldufel

Yeah, it works. You're right, the movement isn't as smooth as before, but for a low-res old school game it's not that crucial...
So, no hurry - I just wanted to know if there's something wrong with the module or I'm just stupid  ;)

And thanks again for your fast help!

Olleh19

Hey eri0o!

Just tried it in my upcoming game, and some issues occured.. And here is what happens if you try to use this module in a game sorta like Day of the Tentacle or Thimbleweed Park where you have different playable characters. The Parallax does not seem to recognise the character change, so the parallax effect gets stuck. Maybe it's just me, that don't understand how to do it, but just maybe you could do some kind of check for what player character is active, so the parallax follows that specific player.

Obviously since i've played Thimbleweed Park lately i was excited to try parallax scrolling, however when running in the Thumbleweed Template, the player gets transported back in the picture when running to the sides, so it looks funny. (laugh). Also the Parallax scrolling is stuttery looking, maybe that could be solved if i understood how to change the speed of the parallax, i'm not sure (Not using any objects, just followed the intro post's posy, posx in properties).

Btw, is there a manual of it somewhere? The syntax you wrote in the intro post, i just don't get how to use those in the scripts, it's way over my head, so examples would be great. I've tried several ways that i thought might would work, but to no success. However i did follow the instructions

Thanks, and i hope you deicide to keep working on this, tho it is a really cool feature for usage in AGS games  :)


eri0o

Hey Olleh19, I still need to figure out Flugerdufel problem. I use this module in my game, like my main use case is myself, which means if the problem doesn't affect me I tend to postpone solving - most from simply lack of time.

So you need to set the target character, basically the variable player points to a character, say cBob, when you assign player as target character, Rellax.TargetCharacter = player, you are actually assigning Rellax.TargetCharacter = cBob. Let's say now you change the player to cAlice, cAlice.SetAsPlayer(), NOW the player pointer will point to cAlice. But you have previously assigned the TargetCharacter as cBob. So you need to actually do something like this when changing player character:

Code: ags
cAlice.SetAsPlayer();
Rellax.TargetCharacter = player;


About parallax scrolling being stuttery it will depend on many things so I can't comment. I recommending using SetGameSpeed(60). Basically the parallax is an illusion and the math is made simple, the object posx/posy (X position, Y position) is increased/decreased (divided by 100, this way I mentally think in percentages) an additional amount when the camera scrolls. The code is simple enough to be posted below, I find it easy to follow. The pxo are the room objects, the variable naming is stolen from Ali's module.

Code: ags
void doObjectParallax(){
  int camx = _final_cam_x;
  int camy = _final_cam_y;

  for(int i=0; i<_pxo_count; i++){
    float parallax_x = IntToFloat(_pxo[i].GetProperty("PxPos"))/100.0;
    float parallax_y = IntToFloat(_pxo[i].GetProperty("PyPos"))/100.0;

    _pxo[i].X=_pxoOriginX[i]+FloatToInt(IntToFloat(camx)*parallax_x);
    _pxo[i].Y=_pxoOriginY[i]+FloatToInt(IntToFloat(camy)*parallax_y);
  }
}


So you set the room objects custom properties, and then you basically only use Rellax.EnableParallax = true on Room Load and you are done.

Olleh19

#25
Quote from: eri0o on Tue 15/09/2020 00:11:18
Hey Olleh19, I still need to figure out Flugerdufel problem. I use this module in my game, like my main use case is myself, which means if the problem doesn't affect me I tend to postpone solving - most from simply lack of time.

So you need to set the target character, basically the variable player points to a character, say cBob, when you assign player as target character, Rellax.TargetCharacter = player, you are actually assigning Rellax.TargetCharacter = cBob. Let's say now you change the player to cAlice, cAlice.SetAsPlayer(), NOW the player pointer will point to cAlice. But you have previously assigned the TargetCharacter as cBob. So you need to actually do something like this when changing player character:

Code: ags
cAlice.SetAsPlayer();
Rellax.TargetCharacter = player;


About parallax scrolling being stuttery it will depend on many things so I can't comment. I recommending using SetGameSpeed(60). Basically the parallax is an illusion and the math is made simple, the object posx/posy (X position, Y position) is increased/decreased (divided by 100, this way I mentally think in percentages) an additional amount when the camera scrolls. The code is simple enough to be posted below, I find it easy to follow. The pxo are the room objects, the variable naming is stolen from Ali's module.

Code: ags
void doObjectParallax(){
  int camx = _final_cam_x;
  int camy = _final_cam_y;

  for(int i=0; i<_pxo_count; i++){
    float parallax_x = IntToFloat(_pxo[i].GetProperty("PxPos"))/100.0;
    float parallax_y = IntToFloat(_pxo[i].GetProperty("PyPos"))/100.0;

    _pxo[i].X=_pxoOriginX[i]+FloatToInt(IntToFloat(camx)*parallax_x);
    _pxo[i].Y=_pxoOriginY[i]+FloatToInt(IntToFloat(camy)*parallax_y);
  }
}


So you set the room objects custom properties, and then you basically only use Rellax.EnableParallax = true on Room Load and you are done.

Thanks for your reply! I've used setplayer a lot of course, can't believe i didn't try that one! (doh!!). (laugh)
I'll play around with gamespeed aswell, thanks and once again keep working on it. I think it could be really cool if in the future it was inside the thumbleweed template. Since it's missing the parall feature.  :)

Edit: Rellax was it, now i'm making it look way better turns out all i needed was CameraLookaheadX and a minus value!


TheVolumeRemote

Love Rellax (Thank you!). I have a question, I have one scene which for comedic timing requires very fast cuts, immediate back and fourth between two players trading off as SetAsPlayer. It works w the default/stock AGS settings but with Rellax it’s so smooth, the comedic timing is lost.

Is there a way to disable Rellax during a function/midway in a rooms script? It tried
Code: ags
Rellax.EnableSmoothCam = false;

But it doesn’t then use default AGS camera movement so it no longer targets/tracks the set player.

I also tried
Code: ags
Rellax.CameraLookAheadX = 100;

With various numerical values, the idea was to move it so fast to the newly set player that it would look like a quick cut. No luck though.

Finally I tried making a duplicate/identical room just for the part I need quick cuts and even with enablesmoothcam off at room load, AGS’s default camera movement isn’t kicking in.

So long short, is there a way, even if it’s with default/stock AGS camera commands, to have Rellax working in a room and then for one segment, have it “off” for quick camera cuts? Alternatively, instead of turning it off and using AGS commands, perhaps Rellax can do quick cuts with a setting?

Thank you for any tips!

eri0o

I think you are mixing what the module does and what ags cameras do.

AGS Cameras have a property called AutoTracking which will make them follow whoever is the player.

https://adventuregamestudio.github.io/ags-manual/Camera.html#cameraautotracking

If you move the camera in other ways, like directly setting a camera position, it will disable this tracking, which is necessary for the module.

https://adventuregamestudio.github.io/ags-manual/Camera.html#camerasetat

Your default game camera can be accessed under Game.Camera property. When you disable the rellax camera, if you want it to return the default following you can just set AutoTracking to true.

I usually do my Camera movement that is not through rellax using the Tween module - because I usually want the smoothness - but it's interesting that you have a use case without smoothness.

There's a constant inside rellax that sets the smoothness, perhaps I could expose it too in case people want to tweak it.

Tarnos12

Hey,

What happened to the module?
All links are broken, github repo is no longer available.
Is there a way to get this module?

Thanks ;]

Snarky



Hobbes

Hi Eri0o, recently gave the rellax module a go and it's phenomenal! The smooth scrolling of the camera is an amazing addition to my game.

I was curious though, is there a way to temporarily disable rellax? Certain cutscenes in my game would play better without, so if there was a rellax.disable() command or something similar, that would be great. Not sure if I'm looking in the right place for it! Any help you can give would be most appreciated. Thanks for creating this module in the first place!

eri0o

Hobbes, try Rellax.EnableSmoothCam = false; , there's also a separate one for disabling parallax scrolling - you may want to disable camera smoothing but keep parallax scrolling going if you are controlling the camera with a Tween for example.

Hobbes

That did the trick - thank you!

Icey

Hey again, eri0o!  :grin:

I wanted to try to get the smooth scrolling module up that Ali made up and running and I found my way here after having some weird kind of problem with it.
I figured I'd give your module a go, and lo and behold, I'm having similar problem with AGS getting confused over some type of property issue?
(I see there is two other after it as well, but that "PxPos" one was a problem in both modules.


Crimson Wizard

@Icey, you need to read the description given in the first post (under the spoiler), it explains how to setup "PxPos" custom properties.

Custom properties are the way to add new values to game objects. Topic covering custom properties may be found in the manual: https://adventuregamestudio.github.io/ags-manual/CustomProperties.html

eri0o

@Icey, what CW said, the how-to, it's in the first page!

An important thing from that page that could be useful to you:

an object with Px and Py 100 will be fixed on the screen despite camera movement.

This is about your other question in the other topic.

Icey

That's interesting! I remember using this previously (if I'm correct) I was able to do load in the module and it would just work. So I wasn't sure if I could if was because of a new version of AGS or something else on my end.

I'll make sure to refer to that guide, and also thanks for the tip about Pxy 100. I was most likely going to lead to another question haha.

Laura Hunt

Hi eri0o,

I'm trying out your module only for the smooth camera tracking functionality (I don't need parallax for now, so I switched it off), but the behaviour is kind of weird. As you can see, the camera is not tracking the character as it walks, but only when it stops walking, resulting in a very jittery movement, rather than the smooth continuous scroll I was hoping for:



Any idea what could be happening here? I have tried adding some camera lookahead on the Y axis (since the room only scrolls vertically), but it makes no difference. I'm using AGS 3.5.1 (patch 4).

eri0o

#39
It's probably the lerp factor here

https://github.com/ericoporto/rellax/blob/a6d532a4d938833709cd5c618216b049461f4593/rellax.asc#L272

I will come up with a way to expose this as a property.

I need to check since when I made this module for my problem with faster walkspeed in mind - like the platformer in the demo. This is the same problem previously reported but linked above.

Relax works like this (from memory), there's a 40x40px box at the center of the screen, when the character is outside of this box things happen to attempt to put it back in the box. I think for lower speeds I need to also expose this property to reduce this window size, this is made so it's easier to do precision jumps in a platformer or other precise movements without the window scrolling, but 40px window may be too big depending on game resolution - hence the need to expose it.

Ah it can also be the still ticks. Can you make your game 60fps instead of 40fps? It should take half a second on a 60fps game, I can also expose the still ticks, these were made for when a character falls it falls downwards, it only matters if the movement is done without leaving the box borders.

Edit:

Later today I will do a new release that should ease understanding, tuning the camera and fixing issues.

SMF spam blocked by CleanTalk