Map Screen a la Flight of the Amazon Queen...?

Started by Bandersnatch, Tue 13/03/2012 14:47:08

Previous topic - Next topic

Bandersnatch

After playing through (and thoroughly enjoying) Flight of the Amazon Queen, I've decided I want to utilise a map screen similar to the "Pinnacle" room. So rather than a literal map or aerial view we have a scrolling vista controlled by mouse movement with a foreground element and player character in front. But as always, I'm new here and don't really know how to accomplish that...

Any idea how to carry this out?

Victor6

#1
It's actually a bit simpler than you'd expect. although this probably isn't the best method.

In AGS, you would set the player characters view to something invisible (a blank sprite), and then add the following to room_RepExec()

cEgo.x = mouse.x;
cEgo.y = mouse.y;

AGS will automatically scroll the background so the player character (which is under the mouse pointer) stays centred.

The character image in the center of the screen isn't the one the player is controlling. It's a background object that looks like him (or her). For the sake of the example we'll assume it's object[0].
If you want to keep it centered, you'd add;

object[0].X = mouse.x;

You'd probably want a couple of hard caps on this too, to prevent the object running too far to the left or right. so add;

you might need to adjust a few things to fit your game, this example is for a game running in 640*480, with a background 1280 (2 screens) wide.

if (object[0].X < 960) { object[0].X = 960; } // right - stops the character moving above 960
if (object[0].X > 320) { object[0].X = 320; } // left - stops the object moving below 240

If any of the above turns out to be my usual nonsense, I apologize in advance.

Edit; - I forgot something. You need to factor in the width of your background object. So if he's 50 pixels wide, you need to subtract 25 to keep it centered.

Bandersnatch

#2
Thanks, Victor6! I'll give it a try. As I'm such a newbie with code it still sounds fairly complex, but you seem to have explained it well enough!

EDIT: I don't want the character to stay rigidly centred. In FOTAQ, he (and the rock he's standing on) scroll in the opposite direction to the background, and to a much smaller extreme. How would that be done?

Victor6

With a whole bunch of math;

The following code is really, really shoddy. but it'll do as an example. Again with a 1280 wide background


Code: ags

// Insert this after you've moved the player to the mouse cursor.

if (mouse.x > 640){ object[0].X = 640 + ((mouse.x-640)/2); // right
else if (mouse.x < 640){ object[0].X = 640 - ((640-mouse.x)/2); // left


The first line checks if the mouse cursor is over the center point of the background, and if it is, it sets object[0].X to 640 (the centre) + half of the remaining distance: (cEgo.x-640) / 2. This would cause the object move at half speed.

So if the mouse cursor was at say 960, the object would be moved to 640 + ( (960-640) / 2 )= 800. 

the second line does the same, but in reverse;-

if the mouse cursor is less that 640, then object[0].X = 640 - half the distance from the centre (( 640 - mouse.x ) / 2 )

If the cursor was at 152, the object would be at 640 - ((640 - 152) / 2) = 396
If the cursor was at 320, the object would be at 640 - ((640 - 320) / 2) = 480
If the cursor was at 520, the object would be at 640 - ((640 - 520) / 2) = 580....and so on.

If you want to reduce the speed, adjust 2 accordingly (higher numbers give a slower movement)

I'm sure I've forgotten something again.....


Khris

I'd do things a bit differently.

Moving around a character which in turn scrolls the screen works, but why not set the viewport directly?

Code: ags
// in room_RepExec()

  Object*o = oPinnacle;  // foreground object (character on pinnacle)
  int bg_width = 960; // room background width

  int vp_width = System.ViewportWidth;
  int max_viewport_x = bg_width - vp_width;

  // scale mouse.x to viewport x, with a small border at the edge of the screen
  int border = 20;
  int x = mouse.x; if (x < border) x = border; if (x >= vp_width - border) x = (vp_width - border) - 1;
  x -= border;
  x = (x * max_viewport_x)/((vp_width - border*2) - 1);

  // move viewport
  SetViewport(x, GetViewportY());

  // place object
  int obj_x = x + (vp_width - Game.SpriteWidth[o.Graphic])/2;
  int obj_offset = x - max_viewport_x/2;
  obj_x -= obj_offset/3;           // change this to in/decrease object scrolling
  o.X = obj_x;

  return;  // remove this line to animate the pinnacle object

  int first_sprite = 72;  // sprite slots, from looking to the very left
  int last_sprite = 76;  // to looking to the very right

  int sprite_offset = (mouse.x*((last_sprite - first_sprite) + 1))/vp_width;
  o.Graphic = first_sprite + sprite_offset;

(Tested, working)

This should take care of everything.
Enter the object's script name and room width near the top, and the sprite numbers for the pinnacle object near the bottom of the function (remove the return line in that case).

Edit: Started this before Victor6's second post, posting anyway.

Victor6

Heh, I was waiting for Khris to turn up with a logical method....

Looked into an old project and remembered what I forgot;-

mouse.x is relative to screen position, actors and objects x variables are not. So you get a limited traverse unless you multiply. In my first test, it didn't matter, because the range of the traverse is 640, and the background was 1280 wide (2 screens).

so;
1. You'd need to set the mouse position first, in room_Load()
mouse.SetPosition(320, 240);

2. factor in your scaling factor dependant on the background size.
i.e for 2560 or 4 screens, you'd want something like
cEgo.X = mouse.x*4

Mucking around with this has given me a few ideas about using multiply objects (or just use the Draw functions) moving at different speeds for a parallax scrolling effect, since you could set up the scenery so that certain parts are only visible when the mouse is between certain co-ordinates (and then when you go back to click on them, they're covered up again....)

Bandersnatch

#6
Almost there, but I'm getting this error:

Error (line 4): undefined symbol 'oPinnacle'

...

Why might that be?

EDIT: My mistake. I set oPinnacle as the description, not the object script name.

ANOTHER EDIT: Finished implementing the code and it works like a dream! Thanks guys, what a great job!

Bandersnatch

Just another thought...

Am I right in believing that you'd use something similar to the "//place object" section of the code to add foreground elements to a regular scrolling background that scroll at a different rate, giving the room some nice depth?

SMF spam blocked by CleanTalk