Screen "Scrolling"
Simsimius

Well, here's the story.

I decided to make my first game that uses an area larger then the default game screen. Since I've never done this before, I got the basic game template I always use. So the game has a black background, and one sprite.

Because in order to create a larger area I defined the size of the virtual screen.

#include <allegro.h>

#define VIRTUAL_W 2400;
#define VIRTUAL_H 1800;

And in my movement code, I have this:

1void GetControls()
2 {
3 if(key[KEY_UP]) { if(ypos - speed < 0) ypos = 0; else ypos -= speed; }
4 if(key[KEY_DOWN]) { if(ypos + speed + sprite->h > SCREEN_H) ypos = SCREEN_H - sprite->h; else ypos += speed; }
5 /*
6 if(key[KEY_LEFT]) { if(xpos - speed < 0) xpos = 0; else xpos -= speed; }
7 if(key[KEY_RIGHT]) { if(xpos + speed + sprite->w > VIRTUAL_W) xpos = SCREEN_W - sprite->w; else xpos += speed; }
8 */
9 if(key[KEY_RIGHT]) {CPlayer1::move_right();}
10 if(key[KEY_LEFT]) {CPlayer1::move_left();}
11
12 }
13
14
15void CPlayer1::move_right() {
16if(xpos + speed + sprite->w > SCREEN_W) xpos = SCREEN_W - sprite->w; else xpos += speed;
17if(xpos == SCREEN_W - sprite->w) {scroll_screen(800, ypos);}
18//CPlayer1::animate();
19}
20void CPlayer1::move_left() {
21if(xpos - speed < 0) xpos = 0; else xpos -= speed;
22//CPlayer1::animate();
23}

I'm trying to get the current screen to go the next area (Normally, X=0. I want the X=800, which is the next area/screen).

Any help with what I'm doing wrong?

James Stanley

Can we see your scroll_screen function?

Simsimius

I'm using the Allegro scroll screen:

int scroll_screen(int x, int y);

Quote:

Attempts to scroll the hardware screen to display a different part of the virtual screen (initially it will be positioned at 0, 0, which is the top left corner). You can use this to move the screen display around in a large virtual screen space, or to page flip back and forth between two non-overlapping areas of the virtual screen. Note that to draw outside the original position in the screen bitmap you will have to alter the clipping rectangle with set_clip_rect().
Mode-X scrolling is reliable and will work on any card, other drivers may not work or not work reliably. See the platform-specific section of the docs for more information.

Allegro will handle any necessary vertical retrace synchronisation when scrolling the screen, so you don't need to call vsync() before it. This means that scroll_screen() has the same time delay effects as vsync().

Return value: Returns zero on success. Returns non-zero if the graphics driver can't handle hardware scrolling or the virtual screen is not large enough.

See also: set_gfx_mode, show_video_bitmap, request_scroll, request_video_bitmap.
Examples using this: exscroll.

So, any ideas on what I've done wrong?

EDIT: If you can't reply to this post, then PM me.

LennyLen

Well, since scroll_screen() can only be called in your code when (xpos == SCREEN_W - sprite->w), I'd be checking whether or not this expression is ever true.

Simsimius

Great idea!

I entered this code in the character class:

if(xpos == SCREEN_W - sprite->w) {TRACE("Xpos DOES equal SCREEN_W - sprite->w");}

and the output file does say "Xpos DOES equal SCREEN_W - sprite->w".

So it does turn out to be true.

I must be doing something wrong with the scroll_screen.

Any ideas?

LennyLen

Did you read the following part of the scroll_screen() section of the manual?

Quote:

Mode-X scrolling is reliable and will work on any card, other drivers may not work or not work reliably. See the platform-specific section of the docs for more information.

[edit]You should really be checking the return value as well.

Simsimius

Return value for what?

And I can't seem to find the relevant information in the docs about "Mode X".
Is there any code to find out if it works on my computer?

Sorry for asking too much, but I'm still a "beginner".

Ariesnl

Why don't you make a "camera" and draw everything according to your camera position ?
That way you can scroll easily and you won't be drawing anything that can't be seen

[one moment'I'll grab my code]

gnolam

Mode X is an archaic DOS graphics mode.

Anyway, the usual way of doing scrolling is not by using virtual screen space and the *_scroll() functions, but by changing the position of objects you blit to the screen.

LennyLen
Quote:

Return value for what?

The return value of the call to scroll_screen.

Try this:

if(xpos == SCREEN_W - sprite->w) {

    if (scroll_screen(800, ypos) != 0)
        allegro_message("scroll_screen() failed!\n");
        exit(-1);

}

Ariesnl
void TSprite::draw(int camx,int camy)
{
int DrawX=int(screenmid_x+(x-camx));
int DrawY=int(screenmid_y+(y-camy));
// only draw what we see
if ((image!=NULL)&&(DrawX+(image->Width()/2)>0)&&(DrawX-(image->Width()/2)<800)&&(DrawY+(image->Height()/2)>0)&& (DrawY-(image->Height()/2)<600))
   {
     image->BlitRotated(DrawX, DrawY,angle,opacity); 
   }
}

this is my sprite drawing code .. the camx,camy coords are the MIDDLE of your viewscreen so if there is a spaceship at (10500,600) and your camera is also at (10500,600) the spaceship will appear in the middle of your screen

and sprite coords are the middle of the sprite NOT top left... why ? easier to make the aiming and docking AI code ;)

Simsimius
Quote:

gnolam
Anyway, the usual way of doing scrolling is not by using virtual screen space and the *_scroll() functions, but by changing the position of objects you blit to the screen.

I think I get what you mean, however, that would be more complicated then having everything written to the virtual screen, and just making the screen scroll.

Quote:

Ariesnl
Why don't you make a "camera" and draw everything according to your camera position ?
That way you can scroll easily and you won't be drawing anything that can't be seen

[one moment'I'll grab my code]

I think you might be on the same idea as gnolam. However, I'll have to wait and see.

EDIT:
LennyLen, thanks for the code. It said it failed.

EDIT2:
Ariesnl,
I don't fully understand your method.

And everyone:
Does anyone have any other suggestions for scrolling without Mode X that operate similar to Screen_scroll.

Ariesnl

look again ;)

Simsimius

I saw it and edited my post.

But I don't fully understand that method. Would you be able to explain in more detail? Thanks.

EDIT: Hold on, I think I got it.

So, the screen remains the same, but you have camx and camy, which can be as large as whatever, and all characters, AI, etc. follow those set of co-ordinates?

Ariesnl

By the way I'm using Openlayer so some functions are diffrent.. However it is highly advised to use OpenLayer...

Quote:

So, the screen remains the same, but you have camx and camy, which can be as large as whatever, and all characters, AI, etc. follow those set of co-ordinates?

... In the oposite direction offcourse

Simsimius

I think I can manage to get a similar method in Allegro.

But I have just wondered something about the initial scroll-screen function:
Was I meant to create a bitmap for the entire virtual screen? I think I had better test this.

Ariesnl

I don't know actually
I used this method even before I ever used allegro, it always works and you only draw what's on screen

Simsimius

The more I think about the method, the more sense it makes.

Hopefully, this is what I plan to do:
Create camx and camy, with the 0,0 being -800, -600 and camy and camx ending at it's 800,600 (not the screens.)

Using this, all objects, code, etc. will be placed according to camx and camy.

The only thing I can't work out is how to scroll, BUT, I think that using x++ and y++ for camx and camy (for when the player moves) will work.

Btw, how do I "reward" people who helped me via the forum? I DO tick the box marked "The question has been answered to my satisfaction!", right?

LennyLen
Quote:

Was I meant to create a bitmap for the entire virtual screen?

Yes, definitely.

[edit]

Quote:

Btw, how do I "reward" people who helped me via the forum? I DO tick the box marked "The question has been answered to my satisfaction!", right?

See previous answer. ;)

Ariesnl

yes just camx++ will sctoll to the left (as the camera goes to the right) and so on

If you check the "question was answered..." you'll get a credit list

BTW what game are you making ?

Simsimius

Lennylen:

Quote:

Quote:
Was I meant to create a bitmap for the entire virtual screen?
Yes, definitely.

How should I go around doing this (just to check if it works).

Ariesnl:
I'm not 100% sure, but I was thinking of a top-down game. Preferably a top-down shooter, but that depends (I've done a space shooter, very, very simple however). Top-down games don't need animations (and I still haven't been able to animate any sprites, and I even tried the forums, but it's no big deal).

LennyLen
Quote:

How should I go around doing this (just to check if it works).

Take a look at the exscroll.c example that came with Allegro. Actually, you should try running ex_scroll.exe, as if it doesn't work, then you'll know that hardware scrolling isn't available on your system.

Simsimius

I will.

Ariesnl:
What do I do if I want to have an object at, for example, the camY and CamX values of -100, 10?

How would I, for example, get the following primitive to be at a certain camC and camY point (I'm using the primitive to test it out)

circlefill(buffer, int x, int y, int radius, int color);

EDIT: This isn't easy. The code you supplied above doesn't seem too easy to understand.

EDIT:
I ran the app, it says

Quote:

Unable to set a 320x240 mode with 640x240 virtual dimensions

EDIT3:
Does anyone else have any more ideas?

Tobias Dammers

Simsimius: Even if the scrolling functions happen to work on your particular computer, I suggest you never touch them again. Seriously.
Scrolling is implemented in hardware, by creating a video bitmap larger than the actual screen, and adjusting the screen pointer to whatever position you want. The problem with this is that there is not a single virtual screen size that is guaranteed to work on any platform; in fact, you might find yourself in a situation where no virtual screen at all is available. And even if there is a virtual screen, you can't rely on the width and height. In 640x480, for example, one card might give you 1024 x 2048, another might do 2048 x 512, yet another might give you 640 x 3000.
The scrolling interface is pretty much a relic from the DOS age, to provide scrolling even when redrawing the entire screen every frame wasn't doable. This way, certain (clumsy) optimizations like the dirty rectangles algorithm are possible. Nowadays, though, an entire screen redraw is seldom an issue, so you might just as well forget that such a thing as a virtual screen exists.
The only appliances for off-screen video bitmaps today are:
- page flipping and triple buffering (2 or 3 video bitmaps)
- hardware-accelerated sprites
A "camera" or "view transform" is (in a simple 2d game, at least) nothing more than a pair of offsets by which you shift the entire scene. For example, if your old drawing code says:

draw_sprite(playerspr, screenbuffer, x, y);

the new code would say:

draw_sprite(playerspr, screenbuffer, x + scroll_x, y + scroll_y);

As long as you apply the same offset to all drawing operations, you achieve the same effect as hardware scrolling.
Note that this gives you an opportunity for optimization, since you only need to draw objects that are actually visible on screen (or at least partly).

Simsimius

So, let me get this straight, the method I originally tried is not recommended, at least by you. ok.

Now, the code you showed me near the bottom looks promising.

So,

draw_sprite(playerspr, screenbuffer, x + scroll_x, y + scroll_y);

The value of scroll_x scroll_y should be the size of the area beyone the usual 800, 600?
So scroll_x could equal, 1600;
and scroll_y could equal, 1200?
And whenever the character walks to the edge of the screen, scroll_x and scroll_y decreases?

Correct?

EDIT: No, hang on, the above can't be right. Would it be possible to explain just a little. It seems simple, but I can't figure it out completely. I just can't think today >_<

Ariesnl

Top down games need animations too but it's simple just have a pImage pointer that will point to the correct frame

1TExplosion::UpdateFrame()
2{
3 if (mytimer<=0)
4 {
5 mytimer=MILLISECONDS_PER_FRAME
6 frame++
7 if (frame>=NUMBER_OF_EXPLOSIONFRAMES)
8 {
9 destroyed=true;
10 }
11 else
12 {
13 pImage=im_Explosion[frame];
14 }
15 }
16 
17}

mytimer will be decreased by some timer function

Simsimius

Thanks for the animation code.
Seems useful.

I'm just still trying to figure out how to scroll. I've got a pen and paper, trying to work out how to get any method of scrolling to work.

Kikaru

My method for animation works like this: you declare an array of *BITMAPS, one for every frame, then a number in the object, like int frame; to say which array element to draw. It works really well with draw_sprite functions too! ;)

Ariesnl

Take one piece of paper... draw something on it

take a second piece of paper .. cut a rectangle hole in it..

put the second one over the 1st one .. ;);D

gnolam
Quote:

EDIT: No, hang on, the above can't be right. Would it be possible to explain just a little. It seems simple, but I can't figure it out completely. I just can't think today >_<

Think of it like this: instead of moving the camera, you're moving the world.

draw_sprite(playerspr, screenbuffer, x, y);
Draw playerspr on the buffer at x,y.

scroll_x = 10, scroll_y = 0;
draw_sprite(playerspr, screenbuffer, x - scroll_x, y - scroll_y);

Draw playerspr at 10 pixels left of the previous position. This is equivalent to the "camera" moving 10 pixels to the right.

Ariesnl

NO!

NEVER just move the world .. always seperate WORLD and SCREEN coords !! Otherwise you'll have a hell of a time implementing intelligence. Try implementing A* when recalculating everything according to a scroll value..

Work with simple World coords and let the draw function decide where to draw according to the camara's centerpoint like I showed you

also if you do something like this

if ((pPlayer!=NULL)&&(!pPlayer->destroyed))
{
    camx=pPlayer->x;
    camy=pPlayer->y;
}
else
{
 // Do some end of game scrolling

}

the camera will follow the player nicely ;)

gnolam
Quote:

NEVER just move the world .. always seperate WORLD and SCREEN coords !!

No shit Sherlock - that's exactly what the concept of a camera does.
Moving the world is, however, just what you're doing when you're transforming world space coordinates into screen space.

Ariesnl

you can't say shit here ? ;D

I guess you're right.. I was thinking about some OpenGL tut I once had that told me to move the world instead of the cam .. and I (inexperienced as I was thought it was smart... it wasn't)

Simsimius

Trying to take into account what you both say, I tried almost all methods here.

The camera wasn't too easy, as I was unable to workout how to implement it in my game (and I found it hard for objects to keep to the cam co-ordinates).

The scroll_x and scroll_y seems to work quite well. I just keep the player fixed in the centre and force the scroll_y and scroll_x to implement depending on what direction the player moves. The only trouble here is the AI. But I suppose, as the code above states, using -scroll_x and -scroll_y for the X and Y for all code, I'm sure it can work, right?

Btw, does this thread get locked if I say the question has been answered?

Ariesnl

In this thread http://www.allegro.cc/forums/thread/587038
I attached some code .. look for the paperclip in my (second ?) post...

Download and view the code and the exe

just ignore the tilemap code if it's too complicated

Simsimius

Thanks for the link.

A lot of code there, but it does seem useful. (Btw, opened the exe. Looks good).

Ariesnl

There's also A* in it you'll need that once you go coding complex AI in a map based game

Simsimius

At the moment I intend to stay simple - at least to get an engine of some sort fully operational, complete with a sample level and graphics, etc.

I intend to create some AI that fire invisible rectangles, which are destroyed with a collision with a wall, and, when collide with a player, cause the AI to attack. Hopefully, it will work. Even if it is just for the sake of getting everything else working.

Evert
Quote:

Because in order to create a larger area I defined the size of the virtual screen.

It's fine to use this feature if available (though probably a waste of time, see below), but you should be aware that not all drivers are capable of doing this.
In practice, Mode-X can do it, which is only available in DOS (real DOS) and Linux and a bit obscure these days, most DOS VESA implementations claim they can do it and X11 can sortof fake it. Windows can't really do it at all.
So in practice you will hardly ever be in a position to use this feature, and it's better to just implement things differently.

Tobias Dammers

Let me take a shot at explaining the camera.

First of all, forget that there is such a thing as a screen. All your object coordinates are "world" coordinates, and can be whatever you want them to be - meters, feet, yards, miles, you name it. For a simple 2d game, you may choose pixels, but for clarity's sake, I'll use meters as an example. Implement all your game logic (object movement and interaction) using this "world" coordinate set. All positions, velocities, sizes, and whatnot, are stored as meters (or whichever unit you chose).

Now comes the drawing part (which, ideally, is separated from logic). To draw the scene, you need to transform "world" coordinates into "screen" coordinates, and the object that achieves this is a camera or camera transform. The camera must do two things: Scale, and translate. Let's assume you specify the camera position in world coordinates; then you'd start with translating. This is easy: Just subtract the camera position from each object's position:

x_trans = x - cam->x;
y_trans = y - cam->y;

Now for the scaling. You need to define a scaling factor for the world units you use, in other words, you need to know how many pixels one unit has. Let's assume 32 pixels per meter:

cam->scalefac = 32;

x_screen = (x - cam->x) * cam->scalefac;
y_screen = (y - cam->y) * cam->scalefac;

And finally, you want the camera location displayed at screen center, not top-left, so we'll add another translation to the mix:

cam->scalefac = 32;

x_screen = (x - cam->x) * cam->scalefac + SCREEN_W / 2;
y_screen = (y - cam->y) * cam->scalefac + SCREEN_H / 2;

There you go. If you happen to choose pixels for your units, the scale factor is 1, so you can then leave out the multiplication step.

I leave it as an exercise for you to reverse the translation (read: find the world coordinates for a given screen position).

Thread #587166. Printed from Allegro.cc