Warning: Constant WP_CRON_LOCK_TIMEOUT already defined in /home2/jemston4/public_html/wp-config.php on line 91

Warning: Constant AUTOSAVE_INTERVAL already defined in /home2/jemston4/public_html/wp-config.php on line 92

Warning: Constant WP_POST_REVISIONS already defined in /home2/jemston4/public_html/wp-config.php on line 93

Warning: Constant EMPTY_TRASH_DAYS already defined in /home2/jemston4/public_html/wp-config.php on line 94
Camera – JEMstone Games

Camera

Second biggest make-or-break component of an RTS is the camera system. If you can’t quickly see what you need to see, the controls for your units aren’t really as important – on account of you can’t reliably see them when you need to. Again, I came up with a list of goals for the camera:

  • Moving the mouse to the edge of the screen should pan the camera in that direction.
  • Using the arrow keys should quickly move the camera.
  • Pressing Middle Mouse should allow you to drag the camera.
  • Double tapping a ControlGroup hotkey should center the camera on that control group.
  • Pressing Spacebar with a unit or group selected should center the camera on your selection.
  • Pressing CTRL or SHIFT + F1-F12 should save the current screen location.
  • Pressing F1-F12 without those modifier keys should center the screen on the saved location.
  • The mouse wheel should allow zooming in and out.

Zooming may or may not be in the final implementation since for a 2D game I’m not sure how much zooming will really add – I don’t plan to allow a massive amount of zoom-out but I may revisit that as well.

Camera Movement
Edge and Drag Scrolling

Implementation

I decided to create a Camera object that will move the camera to always be centered on itself. This will allow saving screen locations to only have to save an x and y coordinate and also centering the camera on a specific spot or unit will be as easy as moving the object.

Edge Scrolling

This actually turned out to be trickier than I expected. I decided that since I was confining the cursor to the window, I should use the window_* functions to get the window dimensions and cursor location, and if the cursor was outside of the confines of the window or right on the very edge of the window, to start scrolling. What I found though was that when the cursor was outside of the window, I could grab the cursor and put it back inside of the window but it could just jump back out and as long as it wasn’t inside of the window, my game wouldn’t respond to it.

Using some debugging I found that window_mouse_get_* would not acknowledge it was outside of the window and would just save the cursor’s last location. However, display_mouse_get_* would allow the cursor location to update outside of the game window. This meant that I had to compare the cursor’s location on the display with the relative location of the window on the display and then determine if the cursor was out of bounds. After that, though, I was happy with the way the scrolling worked.

Key Scrolling

Super easy to implement, just pressing a key moves the Camera. What I found at this stage though was that even though I was clamping where the camera was actually at, the Camera object wasn’t clamped which caused a weird behavior of it not actually being where you’d think it was. That was easily fixed by clamping Camera’s location instead of the “camera.”

Saving Camera Locations

This worked pretty similarly to the Selection saving, except the camera locations all hold an array of the x and y coordinates that are jumped to.

for( var i = 0; i < 12; ++i){
	if( keyboard_check_pressed(vk_f1 + i)){
		// Save
		if( keyboard_check( vk_shift) || keyboard_check( vk_control)){
			SavedLocation[i] = [x, y];
		}else if( SavedLocation[i] != noone){
			var sl = SavedLocation[i];
			x = sl[0];
			y = sl[1];
		}
	}
}

Boy, that’s easy!

Zooming

I set a ZoomMin and ZoomMax variable that constrain the zoom based on the width of the camera. This was simply changing the camera size. I screwed it up at first though because i was trying to compensate for the new camera size by adding half of the difference in size. That kept scrolling to the bottom right though. Eventually I realized that I’m already centering the camera so the compensation is done automatically. Duh.

Control Group Double Tap

For this I had to go back into the Selection code and add a timer that detects when the user is double-tapping. If it’s a double tap, the camera will center on the average of all of the selected units’ location. I may at a later point introduce an algorithm that tries to center the camera on the biggest cluster of units but we’ll see if that’s necessary. One thing I noticed while testing this is that my Select All that was working before was no longer working. After a lot of confusion, I realized that my Select All code was using the viewport variables instead of the camera variables so it would only ever select units in the top left side of the room instead of where the camera was. I’m starting to notice a theme of the cause of all my problems (me).

Spacebar

This was just using the same code as the double tap but averaging the current selection instead of the control group. Super easy.

Drag Scrolling

This was actually something I had kind of forgotten about until this point. It’s weird, because when I’m playing I use this a lot. To implement this, when the middle mouse is pressed down, I save the location of the cursor relative to the window, hide the cursor, and then center the cursor. From then on, as long as the middle mouse is still down, I take the offset of the mouse from the center and multiply it by a DragScrollSpeed multiplier and then recenter the mouse. When the button is released, we return the cursor to the original spot and resume business as usual.

if( mouse_check_button_pressed( mb_middle)){
	DragScrollOriginX = window_mouse_get_x();
	DragScrollOriginY = window_mouse_get_y();
	window_mouse_set( window_get_width() / 2, window_get_height() / 2);
	window_set_cursor( cr_none);
}else if( mouse_check_button( mb_middle)){
	var midx = window_get_width() / 2;
	var midy = window_get_height() / 2;
	x += (window_mouse_get_x() - midx) * DragScrollSpeed;
	y += (window_mouse_get_y() - midy) * DragScrollSpeed;
	window_mouse_set( midx, midy);
}

if( mouse_check_button_released( mb_middle)){
	window_mouse_set( DragScrollOriginX, DragScrollOriginY);
	window_set_cursor( cr_default);
}

The thing that caught me up here was that setting the cursor location doesn’t actually take effect until the end of the step / cycle. This mean that my screen always jumped to the original offset before working properly. This was easily remedied by using else if instead of just ifs, but it’s good to know nonetheless.

Improvements to Selection

I noticed while testing the Camera that selecting moving units was really hard. I decided this was most likely because, if you’ll recall, I was using the left mouse release to check for units selected no matter what. When you play, you expect the unit you press down on to be the one you selected. I went back into the selection code and added a little piece that saves the unit you originally click on (if any) and will use that as long as you’re not drag clicking

Conclusion

So I’m now very happy with the camera and unit selection. It feels very fluid and natural. The Camera is contained to two events and 5 scripts totalling around 150 lines of code. I did have to expand the Selection a little bit, but so far I have both components very well self-contained!

Leave a comment

Your email address will not be published. Required fields are marked *