[Reserved] SDL Usage, Part 2: "Pirates Ho!"

xiaoxiao2021-03-06  38

SDL Usage, Part 2: "Pirates Ho!" Encoding New Game Code Chief Steps

Sam Lantinga and Lauren MacDonellLoki Entertainment Software, Chief Programmer Technical Writers March 2000

content:

Why use C Automake and AutoCONF error handling object Cache logging functions Re-develop code assembly to conclude reference information about the author

Last month, SAM Lantinga and Lauren MacDonell started the initial encoding and graphic design of "Pirates Ho!". In this section of the Diary of this Adventure Population and Role Play Game, the author demonstrates the primary step of using C and various open source tools for game coding. SAM also discusses contents such as object cache, error handling, and logging functions.

In this section of the new game "Pirates Ho!" Series, we will discuss the initial encoding of the game using C and introduces several GNU tools to create some of the required files. Then continue to discuss the error handling, object cache, logging function, and finally put forward several descriptions of how to further carry out.

Why use C in Loki, most games use C or C , which also have a few speed critical routines, but the game can be written in any language. I decided to use C because it is suitable to think about game logic in an object-oriented manner, and because I am used to using C (see "Side Bar").

Static C object static C objects are ideal for global objects in the overall life period, but use them still have some defects. The constructor of the static C object is typically run before the main function, but the operation of the constructor and the destructor is not specified. This means that if they rely on any external conditions, they may not run as expected. Using static C objects in dynamically loaded shared objects is even more dangerous, because the current implementation of GCC (2.95.2) does not call the destructuring function when uninstalling the shared object. The destructor is called when exiting, which will cause the collapse because the code of the destructor is no longer available.

C has been widely supported on different platforms, but still pays attention to the C language characteristics used, because different compilers may, may not, implement or force all ANSI C specifications. During the development of "Pirates Ho!", We will use G on Linux, using Visual C on Windows, and use the free MPW tool on MacOS.

Automake and AutoConf, Linux Fashion Automake and AutoConf are a group of GNU tools that allow automatic configuration of the source code for different compilation environments. SDL offers M4 macros, so you can support AutoConf-over. This macro allows configuration scripts to detect if the appropriate version of SDL is available on the system.

To create new applications using Automake and AutoConf, we follow 6 steps:

Create a ReadMe file. AutoConf will then use this file to verify that the source distribution is complete. This is also helpful to users. Create configure.in. The GNU AutoConf tool reads a configuration file called Configure.in, which will tell it what characteristics need to be found on the system, and what output files need to be generated using this information. Here is the configure.in file for our game: configure.in :# this first line initializes autoconf and gives it a file what it can

# lying for to make the source distribution is completion.

AC_INIT (README)

# THE AM_INIT_AUTOMAKE MACRO TELLS Automake The Name and version Number

# of the software package so it can generate rules for building a SOURCE

# Archive.

AM_INIT_AUTOMAKE (Pirates, 0.0.1)

# We now Have a list of macros Which Tell AutoConf what Tools We need to

# Build Our Software, In this case "make", A C Compiler, and "install".

# I i We Were Creating a c Program, We Well Use ac_proc_cc instead of cxx.

AC_PROG_MAKE_SET

AC_PROG_CXX

AC_Prog_Install

# This is a trick i Learned At Loki - The Current Compiler for the alpha

# Architecture Doesn't Produce Code That Works on All Versions of The

# Alpha Processor. This Bit Detects The Current Compile Architecture

# and sets the compiler flaggs to produce portable binary code.

AC_CANONICAL_HOST

AC_CANICAL_TARGET

Case "$ TARGET" in

Alpha * - * - linux *)

CXXFLAGS = "$ cxxflags -mcpu = ev4 -wa, -mall"

;

ESAC

# UE the macro sdl provides to check the installed version of the SDL

# Development Environment. Abort the configuration process if The configuration

# minimum version We Require isn't available.

SDL_VERSION = 1.0.8

AM_PATH_SDL ($ SDL_VERSION,

:,

Ac_msg_error ([*** SDL Version $ SDL_VERSION NOT FOUND!])

)

# Add The SDL Preprocessor Flags and Libraries To The Build Process

CXXFLAGS = "$ cxflags $ sdl_cflags"

Libs = "$ libs $ SDL_LIBS" # finaryly create all the generated files

# The configure script takes "file.in" and substetutes variables to produce

# "File". in this case we are just generating the makefiles, but this could

# be used to generate any number of automatically generated files.

AC_OUTPUT

Makefile

SRC / Makefile

])

Create acinclude.m4. GNU ACLOCAL Tool Reads a set of macros used from file acinclude.m4, then merges these macros to file AClocal.m4, and AutoConf uses this file to generate "Configure" scripts. For us, we just want to add support for SDL, so we copy the "SDL.M4" file in the SDL release to acinclude.m4. If SDL-Devel RPM is installed, you can usually find the "SDL.M4" file in the / usr / share / aclocal directory. Create makefile.am. The GNU Automake tool uses the makefile.am file to describe the source and libraries for creating the program. When the user runs the configuration script, it uses this description to generate the Makefile.in template that converts Makefile. For us, we have a top directory that contains readme, some documents, and scripts, and a subdirectory "SRC" containing the actual source code. The top-level Makefile.AM file is very simple. It only tells Automake with a subdirectory contains the MakeFile.Am file that needs to be read, and gives a list, which lists other files to the release when building source profiles: makefile.am

Subdirs = SRC

Extra_dist = notes autogen.sh

Source file makefile.am contains actual content: SRC / Makefile.am

BIN_PROGRAMS = Pirates

Pirates_sources = /

Cacheable.h game.cpp Game.h image.cpp image.h logging.cpp logging.h /

Main.cpp manager.h music.cpp music.h nautical_coord.h paths.cpp /

Paths.h screen.h screen.cpp ship.h splash.cpp splash.h sprite.cpp /

Sprite.h status.cpp status.h text_string.h textfile.h widget.h /

Widget.cpp wind.h

Here, we tell Automake, our program is called "pirates", which consists of a large number of source files. Automake builds a C source file with built-in rules and puts them in the generated makefile so we don't have to worry about it, you can concentrate on writing useful code. Subload Configuration Process: Automake needs to copy some auxiliary files, which use these files to detect the current architecture. To follow the Automake, re-run "Automake -a -V -C - Foreign" until it no longer tells you that it is copying the file. Create AutoGen.sh: This step is optional, but provides you with a way, you can re-generate all configuration-related files with only one simple step, and then build the environment configuration software. We use the following script: autogen.sh #! / Bin / sh

#

ACLOCAL

Automake - Foreign

AutoConf

./configure $ *

When the error handles start, I first write is the base class for handling the status of the object:

STATUS.H

/ * Basic Status Reporting Class * /

#ifndef _STATUS_H

#define _STATUS_H

#include "textstring.h"

Typedef enum {

Status_ERROR = -1,

STATUS_OK = 0

} status_code;

Class status

{

PUBLIC:

STATUS ();

Status (status_code code, const char * message = 0);

Virtual ~ status () {}

Void set_status (status_code code, const char * fmt, ...);

Void set_status (status_code code) {

M_Code = Code;

m_message = 0;

}

Void set_status_from (status & object) {

m_code = Object.status ();

m_Message = Object.status_Message ();

}

Void set_status_from (status * object) {

Set_status_from (* object);

}

Status_code status (void) {

Return (M_CODE);

}

Const char * status_message (void) {

Return (m_message);

}

protected:

STATUS_CODE M_CODE;

TEXT_STRING M_MESSAGE;

}

#ENDIF / * _STATUS_H * /

This class provides a method to store the status of the object and propagate the status to the layer that prints the message to the screen. For example, when the sub-picture object definition is loaded from the file, the load function may call the image constructor with the name of the image file. If the image object cannot be loaded, it sets the status to status_error and sets an error message that will propagate to the top layer:

Bool Image :: load (const char * image)

{

CONST CHAR * PATH;

/ * Load the image from disk * /

Path = get_path (path_data, image);

m_image = img_load (path);

Free_path (path);

IF (! m_image) {set_status (status_error, img_geterror ());

}

Return (status () == status_ok);

}

Bool Sprite :: Load (const char * descfile)

{

...

m_frames = new image * [m_numframes 1];

For (int I = 0; i

m_frames [i] = new image (imagefiles [i]);

// this function is in the status base class, and copies

// the status any error message from the image object

IF (m_frames [i] -> status ()! = status_ok) {

Set_status_from (m_frames [i]);

}

}

Return (status () == status_ok);

}

... then in the top layer screen load routine:

IF (! sprite-> load (spritefile) {

Printf ("Couldn't load sprite:% s / n", sprite-> status_message ());

}

This error handling code is used in our game.

Object Cache For shared images and sound samples of synchronous play, I have long realized that several cache algorithms have been taken. I decided to prepare a universal resource manager that caches access to all objects used in the game multiple times.

The first step is to create a universal base class for all cached objects so that these objects can be operated in the cache without knowing the type of object:

Cacheable.h

/ * This Object Can Be Cached in the resource manager * /

#ifndef _Cacheable_H

#define _Cacheable_H

Class Cacheable

{

PUBLIC:

Cacheable () {

REF_CNT = 1;

}

Virtual ~ cacheable () {}

Void AddRef (void) {

Ref_cnt;

}

Void Delref (void) {

/ * Free this Object When it has a count of 0 * /

IF (--ref_cnt == 0) {

DELETE THIS;

}

}

INT refcnt (void) {

Return (REF_CNT);

}

Private:

INT ref_cnt;

}

#ENDIF / * _CACHEABLE_H * /

All cached objects have a reference count associated with them, so each count will increase each time, the count will decrease each time it releases them. In this implementation, when the reference count is reduced to zero, the object is released itself. This allows you to create objects and transfer it to a function of the reference to this object, and then release the object by the creation program, but only the program is created only when you use an object.

This implementation may exist if an object is unexpectedly released. I will add code to the project to capture this situation. Basically, I will retain a separate cache object pool, and record the stack tracking information released last object release, and send a capture signal when detecting an object is released again. This can be implemented by using a set of functions included in GLIBC 2.0 and updated versions, as they can record and print stack tracking information from the application. For more information, see /usr/include/execinfo.h. Note that the destructor of the cached object is a virtual function. This is necessary, so when the base class is released, the appropriate derived classification function will be called. Otherwise, only the base class destructor is called, and the object of the derived class is missing.

Once you create a cache object, you need a cache to save them:

/ * This is a data cache template That CAN Load and UNLoad Data At Will.

Items Cached in this Template Must Be deived from the status class

And Have a Constructor That Takes A FileName As a parameter.

* /

Template Class Resourcecache: Public Status

{

PUBLIC:

Resourcecache () {

m_cache.next = 0;

}

~ Resourcecache () {

Flush ();

}

T * Request (const char * name) {

T * Data;

DATA = 0;

IF (name) {

Data = find (name);

IF (! DATA) {

Data = loading (name);

}

IF (data) {

Data-> addref ();

}

}

Return (DATA);

}

Void Release (T * DATA) {

IF (data) {

IF (Data-> Refcnt () == 1) {

Log_Warning ("Tried to Release Cached Object");

} else {

Data-> Delref ();

}

}

}

/ * CLEAR All Objects from the cache * /

Void flush (void) {

While (m_cache.next) {

Log_debug ("unloading object% s from cache",

m_cache.next-> name);

Unload (m_cache.next-> data);

}

}

/ * Clear all unused Objects from the cache

THIS COULD BE FASTER IF THE LINK POINTER WASN'T TRASHED BY

The Unload Operation ...

* /

Void garbagecollect (void) {

Struct cache_link * link;

INT N_COLLECTED;

Do {

For (link = m_cache.next; link; link = link-> next) {

IF (link-> data-> refcnt () == 1) {

Unload (link-> data);

Break;

}

}

WHILE (LINK);

LOG_DEBUG ("Cache:% D Objects Garbage Collected", n_collected);

protected:

Struct cache_link {

Char * name;

T * Data;

Struct Cache_Link * Next;

} m_cache;

T * Find (const char * name) {

T * Data;

Struct cache_link * link;

DATA = 0;

For (link = m_cache.next; link; link = link-> next) {

IF (Strcmp (Name, Link-> Name) == 0) {

Data = link-> data;

Break;

}

}

Return (DATA);

}

T * Load (const char * file) {

Struct cache_link * link;

T * Data;

Data = new t (file);

IF (data-> status () == status_ok) {

Link = new struct cache_link;

LINK-> Next = m_cache.next;

LINK-> Name = strdup (file);

LINK-> DATA = DATA;

m_cache.next = link;

} else {

Set_status_from (data);

DELETE DATA;

DATA = 0;

}

Return (DATA);

}

Void unload (t * data) {

Struct Cache_Link * Prev, * LINK;

Prev = & m_cache;

For (link = m_cache.next; link; link = link-> next) {

IF (Data == Link-> DATA) {

/ * Free the object, if it's not in us * /

IF (Data-> Refcnt ()! = 1) {

Log_Warning ("Unloading Cached Object In Use);

}

Data-> Delref ();

/ * REMOVE THE LINK * /

Prev-> Next = link-> next;

DELETE LINK;

/ * WE FOUND IT, STOP LOOKING * /

Break;

}

}

IF (! link) {

Log_Warning ("COULDN '' Find Object In Cache");

}

}

}

Using Printf () to debug one of your most powerful debugging tools in your hand is Printf (). I like to use Printf () in the code, instead of cout because it is a standard I / O library part and does not rely on iostream, and iostream is different on different platforms. When capturing a subtle problem, I often find that the code has passed many complicated steps before the root of the problem. I tried to insert the Printf () statement everywhere within the suspicious area of ​​the code to find out what is being performed, and when executed. When cross-compiled into Win32, this is usually the only way to debug issues.

This implementation is relatively simple. I use templates because the cache object will be used with several different types of data, such as images, music, sound, etc. Cache data is saved in a one-way lin list, and if the requested object is not in the cache, then dynamically load the object. When data is no longer required, it will not be released immediately, but it is released to the universal pool to use it in the near future. The cache has a useless information collection function that releases all non-use objects. When developing, I use it to detect whether there is missing object in the code. When performing useless information collection, the list of remaining objects can be traveled to ensure that the objects that should be released will be left.

Logging functions Various logging functions are very useful, they can print debug messages, display warnings to users, and so on. I developed a group of functions used in the game. In the future, we may need to better control the contents of the printed and print time. For example, we may print object cache information without printing a widget constructor / destructor log, and so on.

The following is the code we use:

/ * Several Logging routines * /

#include

#include

Void log_warning (const char * fmt, ...)

{

VA_LIST AP;

FPRINTF (stderr, "warning:");

VA_START (AP, FMT);

vfprintf (stderr, fmt, AP);

VA_END (AP);

FPRINTF (stderr, "/ n");

}

Void log_debug (const char * fmt, ...)

{

#ifdef debug

VA_LIST AP;

FPRINTF (stderr, "debug:");

VA_START (AP, FMT);

vfprintf (stderr, fmt, AP);

VA_END (AP);

FPRINTF (stderr, "/ n");

#ENDIF

}

Note Varargs: va_start (), vsprintf (), va_end (). This allows us to use the features similar to the Printf in the record, allowing the following statements:

LOG_WARNING ("Only% D Objects Left in Cache", Numleft);

These functions can expand into pop-up dialogs or record them in the file, and print to standard error output. We will understand what functions need to future games.

Renovating the code in many cases, if you want to develop code, it is best to use existing code. Whenever you begin encoding a project, you should first search for other similar items on your web. You may find that you don't have to do anything at all - someone has written the code you need.

Freshmeat on the network (see Resources) is a good place. Many open source items are listed on Freshmeat.

For us, we ask the code to load a variety of image formats, and request code to load and play sound and music. Since this paper focuses on using SDL design games, we will look for code that has been designed to use the selected library.

We use the SDL_IMAGE library to load images; use the SDL_Mixer library to load and play audio clips and music (see Resources).

There is also a need for a simple one-way linked list implementation, so we use the "Standard Template Library" (or STL) issued by SGI (see Resources).

"Standard Template Library" has different implementations in different platforms, so we may end in the final game, but now it provides a good set of basic utility objects. Assembly now, we have all the basic components, you can start put something on the screen. We have code for loading images and music, as well as code to screen, and combine these code together. We added a sample flash screen, and Nicholas Vining wrote sample topic music. The following is the result:

Sample flash screen

You need to install the binary sample programs in the retribution mentioned above.

Conclusion We have started the initial code of the game, the effect is very good. So far, we can play the initial animation sequence and some of the basic architectures required for the game have been completed. Next month, we hope that there is a first version of the sea battle game to be tested and there are many pictures of vessels and landscapes.

Reference

Please visit the Pirates HO! Website can download the source code of the sample and binary code:

Snapshot-030500.tar.gz (Source) Article-2-Sample.tar.gz (binary code) "Pirates Ho!" Used library

SDL_IMAGE SDL_MIXER SMPEG "SDL: Let Linux become fun", just in developerWorks: SAM introduces you to SDL, and how it works "use SDL:" Pirates Ho! ", Also in developerWorks: SAM The first article of this series published by Lauren, telling how they design game concept an Introduction to the SDL API

More game development resources:

Freshmeat, a good open source project Source Source Download (Various Price) An organization of Windows, Mac and Linux graphics, programming, and other packages collection

Software for 3D design:

Canoma: Used to quickly create realistic 3D models according to scanned photos or digital photos, but do not have to have a lot of 3D technology The Scitech Graphics Library downloads some of Linux's best games:

Maelstrom Hopkins FBI Descent 2 Civilization: Call To Power MythII: Soulblighter Railroad Tycoon II Game developer's newsgroup A review of MythII: Soulblighter, in LinuxWorld magazine "Games users play", in LinuxWorld magazine "A whole new civilization built on Linux", In LinuxWorld Magazine

About the author sam lantinga is the author of the Simple DirectMedia Layer (SDL) library, is now the Chief Programmer of Loki Entertainment Software, which is committed to producing the best-selling Linux game. His deal with Linux and games started from 1995, engaged in various Doom! Tool transplants, and porting Macintosh game malstrom to Linux.

转载请注明原文地址:https://www.9cbs.com/read-59916.html

New Post(0)