The first tutorial was really just an introduction to what we are doing. It was designed to be extremely simple, and to start off on ground that you may already be familiar with. In this tutorial we will take it a little bit further, and we will clean up our code(which will be important as our project grows).

We are now going to have two seperate files, rpg.c and rpg.h. In the first version of our Special Edition tutorials we some quick and dirty xform routines, and we will also clean those up in this lesson. Most importantly we will add the building blocks for our viewing environment. Before we get into how to do this, I felt I should clear up our new movement options.

Since this is going to be a Role Playing Game and we are going to use the mouse to select options on the screen(much much later) we are not always going to want to move when we move the mouse. I have chosen to allow players to rotate left/right and look up/down by holding down the right mouse button. I am not decided on how to allow a player to move forward/backwards and strafe at this time. Most games seem to have gone the route of using the arrow keys, but I also want to seek an alternate(and more efficient) means of doing so. Any suggestions can be mailed to erasmushurt@earthlink.net. The goal is to somehow allow users to do everything without ever having to touch the keyboard(if that is at all possible). In the meantime you can move forward or backward by using the mouse position up or down without pressing the right mouse button.

There are quite a few changes in this tutorial so I will simply give the complete listing for both rpg.c and rpg.h.

-----------> Start Of Code(rpg.c)



#include <windows.h>

#include <stdio.h>

#include "rpg.h" //our include file

#include "include\Genesis.h"



void MoveCamera(void);

void LoadPrefs(void);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

				   LPSTR lpszCmdParam, int nCmdShow)

{

	LoadPrefs();

	//CreateMainWindow is copies from GTest.

	mainwindowhandle = CreateMainWindow(hInstance, "RPG", CWidth, CHeight);

	//Start the Genesis Engine

	Engine = geEngine_Create(mainwindowhandle, "RPG", NULL);

	
	//get the DriverSystem object. Sort of a container for the valid video drivers

	//on your system

    DrvSys = geEngine_GetDriverSystem(Engine);

	if (!DrvSys) MessageBox(NULL,"No DrvSys", "Error",MB_OK);

	
	//by specifying NULL in the 2nd param we get the first

	//valid video driver object in the DrvSys container

	//For this miserable hack we just assume it is the Software driver

	Driver = geDriver_SystemGetNextDriver(DrvSys, NULL);

	if (!Driver) MessageBox(NULL,"No Driver","Error",MB_OK);

	//Find our selected drive and use it.

	while(1) {

		geDriver_GetName(Driver, &drvname);

		// check to see if it is the correct driver

		if (drvname[0] == ourdriver) break;

		Driver = geDriver_SystemGetNextDriver(DrvSys, Driver);

		if (!Driver) _exit(-1);

	}

	
	//Just as the DrvSys object is a containor for the valid video drivers

	//for your system, a Driver object is a container for the vaild video modes

	//(320x200, 640x480, etc.) for the particular driver.

	//By specifying NULL, as the 2nd param in this call we retrive an object representing

	//the first video mode contained in our Driver object

	Mode = geDriver_GetNextMode(Driver, NULL);

	//This loop is intended to sift thru all the modes until it finds one

	//with dimensions CWidth x CHeight.

	while(1) { 

		if (!Mode) MessageBox(NULL,"No Mode","Error",MB_OK);

		//get the dimension as integers from this Mode object

		geDriver_ModeGetWidthHeight(Mode, &Width, &Height);

		//compare the dimensions, if they are right exit the loop

		//Mode pointing at this Mode object

		if (Width == CWidth && Height == CHeight) break;

		//otherwise get the next Mode object and try again

		Mode = geDriver_GetNextMode(Driver, Mode); 

	}

	
	//tell Genesis the use our selected video Driver in our selected video mode

	if (!geEngine_SetDriverAndMode(Engine, Driver, Mode)) {

		MessageBox(NULL,"Set Driver/Mode failed","Error",MB_OK);

		_exit(-1);

	}

	geEngine_SetGamma(Engine, 50.0f);

	//create a world object from a BSP file. Of course you'll need to edit the path.

	LevelFile = geVFile_OpenNewSystem(NULL, GE_VFILE_TYPE_DOS, "maps\\genvs.bsp", NULL, 

						GE_VFILE_OPEN_READONLY);

	
	World = geWorld_Create(LevelFile);

	if (!World) {

		MessageBox(NULL,"No World","Error",MB_OK);

		_exit(-1);

	}

	//define the rectangle on the main window that the camera will draw into

	//It's in window coordinates, <snort>, that took me a good while to figure out.

	Rect.Left = 0;

    	Rect.Right = CWidth-1;

    	Rect.Top = 0;

    	Rect.Bottom = CHeight-1;

	//create a camera object with an FOV of 2 (clueless), 

	//and Rect window rendering area

	Camera = geCamera_Create(2.0f, &Rect);

	if (!Camera) {

		MessageBox(NULL,"No Camera","Error",MB_OK);

		_exit(-1);

	}

	//build a vector operator for the camera. i guess the camera is a vector.

	//an Xform is a 4x4 matrix that can have both vector rotation and vector translation

	//operations encoded in to them. A vector that is "vector multipled" by one of these

	//matrices will undergo the encoded operations.

	//This call sets the matrix up to do exactly nothing

	geXForm3d_SetIdentity(&Xform);

	//These calls set the Xform matrix to first rotate about the z-axis

	//then about the Y-axis, then about the X-axis. Not by much in this case, but 

	//you understand. One note 2nd param is in radians. (2*pi rads per 360 degs)

    geXForm3d_RotateZ(&Xform, (geFloat)0.0);

    geXForm3d_RotateX(&Xform, (geFloat)0.0);

    geXForm3d_RotateY(&Xform, (geFloat)0.0);

	//NOTICE: We have changed this value to 0,0,0. Our camera's settings will be 

	//changed via ViewXForm

    geXForm3d_Translate(&Xform, 0, 0, 0);

	
	//i guess this does the actual multiplication of the camera vector by the xform.

	//It might reset the camer vector before it does it tho, not too sure.

	geCamera_SetWorldSpaceXForm(Camera, &Xform);

	
	//this loop is intended to be the main rendering and windows message pumping loop

	run = 1;

	while (run) {

		//gather mouse motion data, move camera

		MoveCamera();

		//Begin the engine frame, Engine, World, & Camera better all be valid pointers

		//or its gonna hang.

		if (GE_FALSE == geEngine_BeginFrame(Engine, Camera, GE_TRUE)) return GE_FALSE;

		
		//render to offscreen buffer

		if (GE_FALSE == geEngine_RenderWorld(Engine, World, Camera, 0.0f)) break;

		//end the frame, copy offscreen buffer to onscreen window

		if (GE_FALSE == geEngine_EndFrame(Engine)) break;

		
		

		// Do the'ol message pump (Code fragemt from GTest)

		//read all the windows messages in the task qeque and

		//dispatch the to the window. Note that GetMessage will return false

		//if it gets a WM_QUIT message. In the window message handler rountine

		//we put a WM_QUIT message in our own qeque if the mouse is clicked.

		while (PeekMessage( &Msg, NULL, 0, 0, PM_NOREMOVE))

        {

            if (!GetMessage(&Msg, NULL, 0, 0 ))

            {

				run = 0;

                break;

            }

            TranslateMessage(&Msg); 

            DispatchMessage(&Msg);

        }

        
	}

	//shut it all down and quit

	geCamera_Destroy(&Camera);

	geWorld_Free(World);

	geEngine_ShutdownDriver(Engine);

	geEngine_Free(Engine);

	return (0);

}

//=====================================================================================

//    CreateMainWindow

//=====================================================================================

static HWND CreateMainWindow(HINSTANCE hInstance, char *AppName, int32 Width, int32 Height)

{

    WNDCLASS        wc;

    HWND             hWnd;

    RECT             WindowRect;

    //

    // Set up and register application window class

    //

    wc.style = CS_HREDRAW | CS_VREDRAW;

    wc.lpfnWndProc = (WNDPROC)WndProc;

    wc.cbClsExtra = 0;

    wc.cbWndExtra = 0;

    wc.hInstance = hInstance;

    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

    wc.hCursor = LoadCursor(NULL, IDC_ARROW);

    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);

    wc.lpszMenuName = (const char*)NULL;

    wc.lpszClassName = AppName;

    RegisterClass(&wc);

	//

    // Create application's main window

    //

    hWnd = CreateWindowEx(

        0,

        AppName,

        AppName,

        0,

        CW_USEDEFAULT,

        CW_USEDEFAULT,

        CW_USEDEFAULT,

        CW_USEDEFAULT,

        NULL,

        NULL,

        (HINSTANCE)hInstance,

        NULL);

    if (!hWnd)

    {

        MessageBox(0, "Could not create window.", "** ERROR **", MB_OK);

        _exit(1);

    }    

    UpdateWindow(hWnd);

    SetFocus(hWnd);

    SetWindowLong(hWnd, 

		GWL_STYLE, 

		GetWindowLong(hWnd, GWL_STYLE) & ~WS_POPUP);

    SetWindowLong(hWnd, 

		GWL_STYLE, 

		GetWindowLong(hWnd, GWL_STYLE) | (WS_OVERLAPPED | 

		WS_CAPTION | 

		WS_SYSMENU | 

		WS_MINIMIZEBOX));

    SetWindowLong(hWnd, 

		GWL_STYLE, 

		GetWindowLong(hWnd, GWL_STYLE) | WS_THICKFRAME |

		WS_MAXIMIZEBOX);

    SetWindowLong(hWnd, 

		GWL_EXSTYLE, 

		GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_TOPMOST);

    WindowRect.left = 10;

    WindowRect.top = 10;

    WindowRect.right = 10+Width;

    WindowRect.bottom = 10+Height;

    SetWindowPos(hWnd, 

		HWND_TOP, 

		10,

		10,

		Width+10,

		Height+10,

		SWP_NOCOPYBITS | SWP_NOZORDER);

    //

    // Make window visible

    //

    ShowWindow(hWnd, SW_SHOWNORMAL);

    return hWnd;

}

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage,

						 WPARAM wParam, LPARAM lParam)

{

    switch(iMessage)

    {

	case WM_RBUTTONDOWN:    //is RMB down?

		spin = 1;             //if so then start spinning

    }    

    switch(iMessage)

    {

	case WM_RBUTTONUP:        //is it back up?

		spin = 0;             //then stop spinning

    }

    switch(iMessage)

    {

	case WM_LBUTTONUP:

	case WM_LBUTTONDOWN:

        {

			PostMessage(hWnd, WM_QUIT, 0, 0);

        }

	default:

		return DefWindowProc(hWnd, iMessage, wParam, lParam);

    }

    return 0;

}
void MoveCamera(void)

{

	POINT pos;

	geVec3d Pos; //this value will be used later when we translate our ViewXform

	
	GetCursorPos(&pos);

	ScreenToClient(mainwindowhandle, &pos);

	// are we in spin mode(RMB down) then check for angles

	if (spin ==1)

	{

		
		// checks where the pointer is at(left, right, up, or down)

		// notice that we adjust it according to our height and width, that way

		// it is proper in every resolution;

		if (pos.x > ((CWidth/2) + 50)) {   // is it to the left?

			Angles.Y = Angles.Y-(geFloat)((pos.x-(CWidth/2))*.00005); //if so spin left

		}

		else if (pos.x < ((CWidth/2) - 50)) {//is it to the right?

			Angles.Y = Angles.Y+(geFloat)(((CWidth/2)+pos.x)*.00005); //if so spin right

		}

		
		if (pos.y > ((CHeight/2) + 50)) {//is it to the top?

			Angles.X = Angles.X-(geFloat)((pos.y-(CHeight/2))*.00005); //if so look up

			
		}

		if (pos.y < ((CHeight/2) - 50)) {

			Angles.X = Angles.X+(geFloat)(((CHeight/2)+pos.y)*.00005); //if so look down

		}

	}

	else // we are not in spin mode

	{

		if (pos.y > ((CHeight/2) + 50)) { //is it to the top?

			Xform.Translation.Z += 3.0f; //move forward

		}

		if (pos.y < ((CHeight/2) - 50)) { //is it to the bottom?

			Xform.Translation.Z -= 3.0f; //move backward

		}

		
	}

	//make sure we arent looking too far up or down. If we are then fix that!

	if (Angles.X>.5)

		Angles.X = .5;

	if (Angles.X<-.5)

		Angles.X =-.5;

	
	ViewXForm = Xform; // Copy our Xform into ViewXForm(so we can deform it to 

	// where our player is looking etc)

	
	Pos = ViewXForm.Translation;

	
	// Clear the matrix

	geXForm3d_SetIdentity(&ViewXForm);

	
	// Rotate then translate.

	geXForm3d_RotateZ(&ViewXForm, Angles.Z);

	geXForm3d_RotateX(&ViewXForm, Angles.X);

	geXForm3d_RotateY(&ViewXForm, Angles.Y);

    
	geXForm3d_Translate(&ViewXForm, Pos.X, Pos.Y, Pos.Z);

	
	ViewXForm.Translation.Y += 140.0f; // We give a +140 adjustment to simulate an eye-high view.

    
	geCamera_SetWorldSpaceXForm(Camera, &ViewXForm);

}

// Load our preferences from prefs.ini

void LoadPrefs(void)

{

    stream = fopen("prefs.ini","r");//open as read only

    fscanf(stream,"%s",&ourdriver);//get our information

    fscanf(stream,"%d",&CWidth);

    fscanf(stream,"%d",&CHeight);

    fclose(stream);    //dont forget to close

}
--------> End of Code(rpg.c)
Next we create our rpg.h file.



-------> Start Of Code(rpg.h)

#include <windows.h>

#include <stdio.h>

#include "genesis.h"
static HWND CreateMainWindow(HINSTANCE hInstance, char *AppName, int32 Width, int32 Height);

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
int				spin = 0; //whether or not we are spinning

geVec3d			playerpos;

FILE			*stream;

HWND			mainwindowhandle;

geEngine		*Engine = NULL;

geDriver_System *DrvSys = NULL;

geDriver		*Driver = NULL;

geDriver_Mode	*Mode = NULL;

geWorld			*World = NULL;

geCamera		*Camera = NULL;

geRect			Rect;

const char		*modename = NULL, *drvname = NULL;

MSG             Msg;

geXForm3d		Xform;

geVec3d         Angles;

geXForm3d		ViewXForm;                         

long			run, Width, Height;

geVFile			*LevelFile;

int				CWidth =640;    //Our Clients Width And Height, and driver(loaded from prefs.ini)

int				CHeight=480;

char			ourdriver = 'G';



-------> End Of Code(rpg.h)

In the next tutorial we will implement collision detect.

As always you can send comments or suggestions for this tutorial to erasmushurt@earthlink.net.

[Hyperlinkleisten stehen in diesem Web nicht zur Verfügung]