Workshops

Top  Previous  Next

We all know that A7 comes with a new programming language named lite-C. I'm the type of guy that doesn't like changes, but I have to admit that lite-C has many advantages 

over C-script. First of all, when you are learning lite-C you are also learning pure C as well, so you can get a job as a C programmer! Then, let's not forget about the enormous 

amount of C code samples that can be found on the internet. I have kept the best part at the end: all the power offered by the C / C++ programming language (Windows API, 

DirectX, OpenGl, etc) is now available for your beloved engine!

 

Get ready to roll! Lite-C can be used in two modes: legacy mode and pure mode.

1) If you program in legacy mode, you can't use Acknex's features: the engine doesn't know anything about bitmaps, sounds, panels, etc so you have to write the code that takes care of them by yourself. The good news is that any program written in legacy mode is compatible with other C / C++ compilers so it will be portable, provided that you use the standard C features.

2) If you program in pure mode, Acknex does most of the hard work for you, so the code will be shorter and more easy to understand. You will not be able to compile your code using a C / C++ IDE such as Visual C++ though.

 

We are going to start our journey by writing a few small, legacy mode applications. I am using lite-C 1.05, which can be downloaded freely at Conitec's lite-C page, but if  you own any version of A7 it's even better. Copy the "litec101" folder from inside the downloadable archive to your lite-C / 3DGS folder, start Sed, and the open the hello.c file.

 

#include <litec.h>

 

int WinMain()

{

       MessageBox (NULL, "Hello World!", "My first program", MB_OK | MB_ICONSTOP);

       return (0);

}

 

As you see, the code for our "Hello World" example is extremely simple. First of all, we include the litec.h file; this tells the engine that we are going to create a legacy mode program. Then, we have a WinMain function definition, the entry point for any Windows program, similar to the well known function main( ) from C-Script and lite-C pure mode.

 

WinMain is declared as a function that is supposed to return an integer (int) because the ANSI C standard states that a function must return a value and that's exactly what the "return" line of code does. Aren't you curious to see the program in action? Let's run it right away!

 

aum67_litec1

 

The MessageBox line of code does quite a few things for us: it's a dialog box which displays a message, an icon and one or a few buttons that can be clicked. In our case, NULL is the handle of the owner window; since we don't have a parent window here, the program will use Windows' desktop as the parent window, MB_OK creates the button with the "OK" text printed over it and MB_ICONSTOP displays the stop picture on the message box. You can use things like MB_ICONEXCLAMATION, MB_ICONQUESTION, etc here - just look them up in a Windows API programming book.

 

I have a confession to make: the previous application wasn't a "real" Windows application; you can't change its size, minimize it, etc. The code for a true Windows application is more complicated, but even if it looks scary, don't worry - you'll just copy and paste it in your own projects. Let's open up the basicwinapp.c file:

 

#include <litec.h>

 

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // this is the message loop function

{

       PAINTSTRUCT ps; // needed by WM_PAINT

       HDC hdc; // this is a handle to the graphics context

       switch(message)

       {

               case WM_CREATE: // this message is sent when the window is created, so do initializations here (if needed)

                       break;

               case WM_PAINT: // this message is sent whenever your window's contents need to be painted again

                       hdc = BeginPaint (hWnd, &ps); // initiates window painting

                       EndPaint (hWnd, &ps); // ends window painting

                       break;

               case WM_DESTROY: // this message is sent when the window is about to be destroyed

                       PostQuitMessage(0); // inserts a "quit" message into the message queue

                       break;

               default:

                       break;

       }

       return DefWindowProc(hWnd, message, wParam, lParam); // process the rest of the messages

}

 

int WINAPI WinMain (WINARGS) // WINARGS is defined inside litec.h

{

       MSG msg; // generic message

       char *szClass = "RealWindowsApp"; // this is the name of the window

       HINSTANCE hi = GetModuleHandle(NULL);

       WNDCLASSEX winclass; // holds the class that we're going to create

       winclass.cbSize = sizeof(WNDCLASSEX); // get the size of WNDCLASSEX

       winclass.style        = CS_HREDRAW | CS_VREDRAW; // sets the class style

       winclass.lpfnWndProc = WndProc; // sets the window procedure to WndProc

       winclass.cbClsExtra = 0; // don't reserve extra space in the class structure - it isn't needed

       winclass.cbWndExtra = 0; // don't reserve extra space in the window structure - it isn't needed

       winclass.hInstance = hi; // this is the instance handle of the program

       winclass.hIcon = LoadIcon (NULL, IDI_APPLICATION); // set an icon for our application

       winclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION); // set a small (title) icon for our application

       winclass.hCursor = LoadCursor(NULL, IDC_ARROW); // load a predefined mouse cursor

       winclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH); // set the background color

       winclass.lpszMenuName = NULL; // our application doesn't use any menu

       winclass.lpszClassName = szClass; // this is the name of our class

       RegisterClassEx(&winclass); // register the window class

       // Create a window named szClass with the specified title, style, position, size and handles to parent window, menu, instance and parameters

       HWND hwnd = CreateWindowEx(0, szClass, "Real Windows Application", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 200, 100, 400, 300, NULL, 0, NULL, NULL);

       while (GetMessage(&msg, NULL, 0, 0)) // this is the main event loop

       {

               TranslateMessage(&msg); // send keystrokes to windows

               DispatchMessage(&msg); // send the message to the window proc

       }

       return (msg.wParam); // return to Windows

}

 

The code looks pretty complicated indeed; however, it is fully commented and you don't have to spend time understanding how it works if you don't want to. We define the same WinMain function which sets al sorts of parameters for our program window, and then it creates it using CreateWindowEx. Don't ignore the "while" loop; it is a good place to do some gaming-related stuff in the future.

 

Another important line of code inside WinMain is this:

 

  winclass.lpfnWndProc = WndProc; // sets the window procedure to WndProc

 

This line tells our window that it has to follow WndProc's instructions and guess what? WndProc is the first function inside basicwinapp.c. That function tells the window how to react when it is created, moved, destroyed, etc. Quite a few book authors have done a good job explaining the inner works of a typical Windows program, but I don't bother memorizing all this stuff - I simply copy / paste the code whenever I want to create a new Windows API program.

 

How does our application look? I thought you'd never ask!

 

aum67_litec2

 

I have to admit that I like it; it has the typical Windows buttons, it can be sized, maximized, minimized... But what about an application that does something more interesting? Open the eventloop.c file and run it - the screen should look like this after a while:

 

aum67_litec3

 

This program outputs "Acknex rules!" 100 times per second and (believe it or not) the changes that had to be made to the previous program are very few - take a look!

 

This is the end of function WinMain for the basicwinapp.c file:

 

       while (GetMessage(&msg, NULL, 0, 0)) // this is the main event loop

       {

               TranslateMessage(&msg); // send keystrokes to windows

               DispatchMessage(&msg); // send the message to the window proc

       }

       return (msg.wParam); // return to Windows

 

And this is the end of function WinMain for the eventloop.c file:

 

       HDC hdc = GetDC(hwnd); // get the graphics device context

       while(TRUE) // main loop, uses PeekMessage() instead of GetMessage() to retrieve the messages

       {

               if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // is there a message in the queue?

               {

                       if (msg.message == WM_QUIT) // if we have to quit then do it!

                               break;

                       TranslateMessage(&msg); // send keystrokes to windows

                       DispatchMessage(&msg);  // send messages to the window proc

               }

               // the interesting stuff happens here

               TextOut(hdc, rand()%400, rand()%300, "Acknex rules!", strlen("Acknex rules!")); // draw text at a random location

               Sleep(10); // wait for 10 milliseconds

       }

       ReleaseDC(hwnd, hdc); // release the dc

       return(msg.wParam); // return to Windows

 

Apart from these lines, there aren't any other changes between the two programs. We get the graphics context device (we get access to the device that outputs data on the screen) and then we run the main event loop. If we have a Windows message that needs to be processed, we take care of it. Then, we use TextOut to output the text "Acknex rules!" at random positions on the 400x300 pixels window, every 10 milliseconds. Finally, when the loop stops running, we release the device context and we return to Windows.

 

That's pretty much it for today. I just wanted to show you that lite-C can be used to create standard Windows applications without too much effort. Nevertheless, I have to admit that working with lite-C in pure mode is much more simple and fun - and that's exactly what we are going to do next month!