View Full Version : PyroGineび「 Game Engine
Pyrogine
09-04-2010, 12:38 PM
PyroGine Development (http://pyrogine.com/index.php?page=about_us) is happy to release (v0.3.0) the 2nd public beta release of PyroGine Game Engine (http://pyrogine.com/index.php?page=pyrogine). It's an advanced 2D game engine for PC's running Microsoft Windows and uses Direct3D for hardware accelerated rendering. It's robust, designed for easy use and suitable for making all types of 2D games and other graphic simulations.
Changelog:
Added API support for standcard C/C++ compilers.
Added full OOP support for C++ Builder 2010.
TPGObject.CreateFromObj was referenced in PGDatabase but the method was not declared and implemented.
Pyrogine
09-04-2010, 12:49 PM
PyroGineび「 Game Engine is based around an enhanced COM modal that I'm calling ECOMび「 or Extendable Component Object Modal. COM allows access to classes across the DLL boundary, the extendable specification allows those classes to also be extendable across the DLL boundary. With the ECOM 2.0 spec, I finally got consistent use for all 32bit versions of Delphi (tested down to version 5).
So what does this mean? Even though the whole SDK was written in Delphi 2010, you can use Delphi 6 for example to access and even extended the classes (full OOP) inside the standard win32 DLL on the host side. Full OOP support for C++ Builder 2010 is now in as well as API support for any standard C++ compiler.
Paul Nicholls is currently working on a PyroGine powered game called The Probe (http://pyrogine.com/index.php?action=pmxblog;sa=view;uid=3).
Pyrogine
09-04-2010, 02:26 PM
Short footage from the AstroBlaster demo included in the distro:
http://www.youtube.com/watch?v=tWD6C7YEI1I
Traveler
09-04-2010, 02:36 PM
Hi Jarrod,
I went to your website earlier to look into your engine and I was kind of surprised to see I have to pay for it. Aren't you afraid to scare potential users? I mean it's not a big sum of cash but still. Why should I buy your product and not go for Asphyre or any of the other free libraries, that seem just as capable for the job?
Mind you I'm just curious to know. I've seen/played the demos and from what I can tell it certainly looks pretty neat.
edit: sorry got names mixed up :(
Pyrogine
09-04-2010, 02:52 PM
@Traveler
PG for me is not a hobby, I have a biz providing middleware solutions and will be also making and selling games. All of which will be sold direct from the site. You do not have to pay for the engine if you do not want to use it in a commercial project as it's free for non-commercial use. If you desire to use it in a commercial setting then a indie friendly Registered Developer License is required.
The good thing about game development today is that there are so many great choices and most are free too. I can only speak for my own products of coarse, PG has all the features out of the box to make a complete 2D game and other graphical simulations. I will not argue that PG is more or less than all the other great solutions out there. I will just say it is what it is and a vision of the sort of rapid game development via a middleware solution that I've strived to develop the past 10+ years.
A few features:
Support for all 32 bit versions of Delphi
Support for C++
API layer for binding to other languages
Sprites, Entities, AI, Primitives
Database (MySQL remote|locate, SQLite local)
Audio (mp3 | wav | ogg | many more)
Many, many more great features...
I have not yet had a change to show case all the great features that's available in PG out of the box that makes it, IMHO stand out. I'm working on this however.
Traveler
09-04-2010, 05:17 PM
Ok fair enough. One area (or perhaps two, with physics) I believe where you could really distinguish yourself from any of the other packages is network support. There have been some questions regarding this in the past, but real working solutions are very hard to come by.
What your opinion on this? Would it be doable?
PS. I've correct the error in my previous post
Pyrogine
09-04-2010, 05:59 PM
Hi,
Basic network support is already in. I'm using reliable UDP. There is a persistence object interface included too which combined with the networking layer allows you to send objects (derived from TPGPersistentObject) across the wire. The network layer is lower level so you can build it up to what ever level desired for your project. Looking on my site I think I forgot to mention this. I will get that corrected today.
Physics and scripting are planned as well. I should have scripting in over the next 2-3 days (hopefully).
paul_nicholls
09-04-2010, 09:22 PM
Hi,
Basic network support is already in. I'm using reliable UDP. There is a persistence object interface included too which combined with the networking layer allows you to send objects (derived from TPGPersistentObject) across the wire. The network layer is lower level so you can build it up to what ever level desired for your project. Looking on my site I think I forgot to mention this. I will get that corrected today.
Physics and scripting are planned as well. I should have scripting in over the next 2-3 days (hopefully).
Networking? Nice!!
cheers,
Paul
Pyrogine
09-04-2010, 09:55 PM
Yup. The networking functionality is in the TPGNet object (PGNetworking unit). You can create your own instance or use the instance already created in the global application object PG.
// open a local port
PG.Net.Open(1024);
// send some data
PG.Net.Send("host name or IP address", 1024, SomeData, SomeDataSize);
// set method to receive data
PG.Net.ReceiveEvent := MyClass.ReciveEvent;
// receive data
procedure MyClass.ReceiveEvent(const aHost: TPGString; aPort: Integer; aData: Pointer; aSize: Integer)
begin
// process data
end;
Data will be buffered and sent out in order and continue to send if there is an error until the timeout limit is reached. You can wrap around this what ever you need for your own projects. For example derive a new class that can serialize a TPGPeristentObject to a memory stream then send this over the wire. The ReceiveEvent is overridden to deserialize the data and recreate the object, thus giving you network object persistence.
In addition to UDP, there are some HTTP and SMTP methods as well:
{ Http }
function GetHttp(const aUserName, aPassword, aUrl: TPGString): TPGString; virtual;
function GetHttpResponseText: TPGString; virtual;
function GetHttpResponseCode: Integer; virtual;
{ Smtp }
function SendMail(const aMailAgent, aUserName, aPassword, aUserHost, aSubject, aTo, aFrom, aText: TPGString; aPort: Integer): TPGString; virtual;
Need to post data to a URL and get a response? No problem, just call:
PG.Net.GetHttp(...)
Need to send out some mail within your app? Again, not a problem, call:
PG.Net.SendMail(...)
paul_nicholls
10-04-2010, 03:13 AM
Very nice...I need to figure out an excuse to use the networking!! LOL :)
cheers,
Paul
The PyroGine really is impressive with all that work you've put into it Jarrod. I'm curious however, would it be possible at all to do cross-platform development with it in the future? I know it uses Direct3D, but what about OpenGL or OpenAL in the future?
Oh and I've seen some screenies of The Probe. Lookin' good so far. Nice work Paul. Don't forget to show off your screenshots here on PGD as well.
paul_nicholls
10-04-2010, 08:26 AM
No wories WILL, I will post my progress on PGD too :)
cheers.
Paul
Traveler
10-04-2010, 11:17 AM
This is pretty good news :)
All this talk (here and PM between us admins) is getting me excited again ;)
paul_nicholls
10-04-2010, 12:15 PM
This is pretty good news :)
All this talk (here and PM between us admins) is getting me excited again ;)
LOL! I'm glad we excite you haha
cheers,
Paul
Pyrogine
10-04-2010, 01:51 PM
IPC_______________________________________________ __
Need to be able to communicate with another application on the same machine, no problem, just do:
// init IPC server
srv := TPGIPCServer.Create;
// init IPC client
cli := TPGIPCClient.Create;
// event handler
procedure OnData(aData: Pointer; aSize: Integer; aHWnd: HWND); virtual;
procedure OnConnect(aHWnd: HWND); virtual;
procedure OnDisconnect(aHWnd: HWND); virtual;
// send data
procedure SendMessage(aData: Pointer; aSize: Integer; aHWnd: HWND); virtual;
Using these classes and the event handlers you can do inter-process communication. An example of this is how I communicated between the command-line compiler and the IDE. Rather than capturing the output (which proved to be error prone) switching to IPC gave me more precise control in logical OOP fashion and worked much better.
Database__________________________________________ _______
Need to communicate with a database on a remote server, locally or just create a local database? No problem, you can do:
// create database object
db := TPGDatabase.Create;
// set the type of database
db.Kind := dtMySQL; // or dtSQLite3
// use open for local database
db.Open(...);
// use connect for local & remoate mysql database
db.Connect(...);
// execute a query
db.ExecSQL(...)
// get a query result
tbl := GetTable(....)
If you've done any remote database work you know that it can take some time to make the connection which will normally block your application, again PG has a solution, threaded support:
function ThreadConnect(const aHost, aUser, aPassword, aDatabase: TPGString; aEventHandler: TPGDbThreadConnectEvent): Boolean; virtual;
function ThreadExecSQL(const aSQL: TPGString; const aArgs: array of const; aEventHandler: TPGDbThreadQueryEvent): Boolean; virtual;
function ThreadGetTable(const aSQL: TPGString; const aArgs: array of const; aEventHandler: TPGDbThreadQueryEvent): Boolean; virtual;
function ThreadStatus: TPGDbThreadStatus; virtual;
These methods allow you to connect and query the database by pushing the operation in a background thread and allowing your application to continue running. When the operation completes or fails and event will fire.
Highscores________________________________________ _________
The threaded support allows the TPGHighscore object to send and retrieve high scores without blocking your game. Oh yea, if you want high score support for your game, you can use the TPGHighscore class, which gives basic HS functionality. If you need more you can use the database support to make your own, locally or remote. Using the Highscore feature you can send your scores to a remote database then on your website you can use php for example to display those scores.
TPGHighscore = class(TPGObject)
public
constructor Create; override;
destructor Destroy; override;
function Connect(const aHost, aUser, aPassword, aDatabase, aTable: TPGString): Boolean; virtual;
function Connected: Boolean; virtual;
procedure Close; virtual;
procedure ClearResults; virtual;
function DropTable: Boolean; virtual;
function Post(const aName: TPGString; aScore: Integer; aSkill: Integer; const aLocation, aDate: TPGString): Boolean; virtual;
function List(aSkill: Integer; const aStartDate, aEndDate: TPGString; aLimit: Integer): TPGDatabaseTable; virtual;
function LastError: TPGString; virtual;
function ThreadConnect(const aHost, aUser, aPassword, aDatabase, aTable: TPGString): Boolean; virtual;
function ThreadList(aSkill: Integer; const aStartDate, aEndDate: TPGString; aLimit: Integer): Boolean; virtual;
function ThreadPost(const aName: TPGString; aScore: Integer; aSkill: Integer; const aLocation, aDate: TPGString): Boolean; virtual;
function ThreadState: TPGHighscoreThreadState; virtual;
procedure ClearThreadState; virtual;
public
function GetDB: TPGDatabase; virtual;
property DB: TPGDatabase read GetDB;
function GetDBResults: TPGDatabaseTable; virtual;
property DBResults: TPGDatabaseTable read GetDBResults;
end;
Data______________________________________________ ___
Need to handle large data sets in a sparse fashion, no problem, use a TPGSparseArray:
// create array
dat := TPGSparseArray.Create;
...
// set some data
dat.Cells[99999,13443] := "Wow, this is HUGE";
dat.Cells[2343, 13] := 1234567;
A sparse array is what I use to handle the Unicode character set for the included FontStudio utility. By default a massive array would have to be created to hold the entire Unicode set (max int I think it was using) for each loaded font. NO, no that was to much. Sparse arrays to the rescue.
Pyrogine
10-04-2010, 02:44 PM
Actors____________________________________________ _____
TPGActors - Are the base objects that can exist and have interaction in the game world.
TPGActorList - Is a dynamic double linked list that Actors can live on. An ActorList can process collisions which will call the Actors OnCollide method.
TPGActorScene - Is a object that handle multiple ActorList objects. For example, rather than having one list that has to check collisions against objects that will not collide, you can have multiple lists, one for particles, one for enemies and one for the player. The idea is that you will normally never do collision check for particles so keep them on a separate list making making the collision check more efficent.
TPGAIState - The AI system in PG is based around an event driven state machine and TPGAIState is an object that represents this logical state.
TPGAIStateMachine - Is an object that manages all the AI states. It can changes states, got back to the previous state, have a global state that can run in parallel with the current state and so on.
TPGAIActor - Is an actor that has a state machine to manage it's states.
TPGEntity - Is an AIActor that can exist in the game world, it has shape (based on sprites), position, can do collision, can be animated so on. You will use an entity most of the time.
An Entity can do [b]PolyPoint collision. It's a precise and fast way to do collisions. Basically the sprite images will be auto traced to make a polygon outline around them. Then the line segments that make up the polygon will undergo a fast 2D intersection test to see if a collision occurs. Before this however, a very fast spherical check will be performed first to make sure the objects are close, if so the more precise PolyPoint check if performed. The auto trace feature took a lot of time to get working correctly and a guy I met from Russia finally help me get it working properly. I can not remember his name now. If your reading this, respect goes out to you dude. PM me.
Sprites___________________________________________ ______
The TPGSprite class can manage a list of textures in a local fashion. It will allow you to define your images into pages and groups. A page is the texture file loaded in, a group is the images on this texture. It is more efficient to have multiple images on one textures than to have one image per texture. So TPGSprite will allow you to add images to a group based on a grid layout or if you need to tightly pack your images into the available space you can specify a rectangle. So all the images added to a group becomes an animation if you wish. When you create an entity, you pass in a sprite object and the group the images belong to.
// init sprite
FSprite := TPGSprite.Create;
// define boss sprite
page := FSprite.LoadPageFromArchive(gTestbed.Archive, 'media/sprites/boss.png',
PG_ColorKey);
group := FSprite.AddGroup;
FSprite.AddImageFromGrid(page, group, 0, 0, 128, 128);
FSprite.AddImageFromGrid(page, group, 1, 0, 128, 128);
FSprite.AddImageFromGrid(page, group, 0, 1, 128, 128);
// init boss entity and add to scene
entity := TBoss.Create;
entity.Init(FSprite, group);
Scene[1].Add(entity);
// define figure sprite
page := FSprite.LoadPageFromArchive(gTestbed.Archive, 'media/sprites/figure.png',
PG_ColorKey);
group := FSprite.AddGroup;
FSprite.AddImageFromGrid(page, group, 0, 0, 128, 128);
// init boss entity and add to scene
entity := TFigure.Create;
entity.Init(FSprite, group);
Scene[0].Add(entity);
Pyrogine
10-04-2010, 05:36 PM
The PyroGine really is impressive with all that work you've put into it Jarrod. I'm curious however, would it be possible at all to do cross-platform development with it in the future? I know it uses Direct3D, but what about OpenGL or OpenAL in the future?
Thanks. It is certainly a possibility. It will be a lot of work, but once I get things stable on the Windows side and I can begin to think about cross-platform design. I will have to rework a lot of stuff because the core of PG has been in development since the early 2000s (whoa, that seems so long ago already) from a Windows/DirectX perspective. I'm committed to cross platform support. A future version of Delphi (NDA and all that stuff prevents me from talking about the specifics) may help make this a magnitude easier.
paul_nicholls
10-04-2010, 09:27 PM
WOW! awesome :)
@sparse arrays: I believe that Font Studio fonts only use a Word array, so 65536 only...
cheers,
Paul
Pyrogine
10-04-2010, 10:39 PM
Yea... a Word array that was 65536 for each font you loaded. I modified it to use my sparse array instead to save a ton of memory.
DLLs
Want to bind a procedural variable to a routine in a DLL? Do this:
var
proc: procedure(msg: pchar);
...
PG.DLL.Bind(proc, "myproc", "myproc.dll");
The TPGDLL class will access the dll, see if it's already loaded, if so use it, if not load it in and bind the variable to the specified exported routine. This object can manually load a DLL, return the count and names of all loaded dlls.
Attributes
All classes in PG are derived from TPGObject and this base class implements Attributes. Each object can have up to 256 attributes associated with it. An example of use would be using it to classify enemies or subclass objects in general.
Obj.Attributes[10] := True;
The actor list system allows you to act on a group of actors base on their attribute values. If you wanted to remove all the objects on the list or query just those objects you can do so by checking if the attribute is set.
Graphics
PG has many graphics related classes such as:
TPGRenderDevice - Manages low-level Direct3D rendering. Supports viewports, swap chains, and primitives.
TPGTexture - Represents a Direct3D texture. Various rendering methods, render targets, direct pixel access, copy to/from textures/surface
TPGSurface - Represents a Direct3D surface. Can be any size, does not support direct rendering, but bits can be copied to a texture to be rendering.
TPGPolygon - A multi segment polygon object. Can be scaled, rotated and transformed.
TPGPolyPoint - Implements the PolyPoint collision system.
TPGSprite - A manager for texture images. One sprite object is capable of managing all images in your project.
TPGImage - Allows rendering of very large images. For example want to render via hardware a 800x600 non-power of two sized image, use TPGImage.
TPGBezier - Implements a brazier object.[b]Audio
A robust audio system that can play most music formats including MP3, OGG, WAV, MOD, S3M and IT. All music formats are treated in the same way, they can be loaded and played as a sound effect or streamed and played as music. So you can have compressed sound effects as ogg files rather than WAVs if you want. You can even stream music from within an archive. Since password protected zip files are supported, applying a GUID for your password to keep your resources secure, your music can be played directly from this archive. Included also is a music player that allows you to playback a list of music on disk or from an archive. If you wanted to have the option of allowing the user to play their own songs, you can do it with PG. You can control the channels, frequency, panning, volume, global volume, control if the music continues to play if the application focus is lost and even dynamically update volume, panning and frequency in real-time based on distance. The music will be updated automatically in a background thread so there is no need to manually call PG.Audio.Update for manual processing.
paul_nicholls
10-04-2010, 11:54 PM
Thanks so much for all this info Jarrod! I've copied it into a .rtf document for my convenience :)
cheers,
Paul
Pyrogine
11-04-2010, 12:18 AM
No problem. I wanted to touch a bit on some of the major parts so readers could get an idea of the range and depth of the engine. I would like to round out things with a list of all the classes available in the SDK.
TPGObject
TPGObjectList
TPGDLL
TPGActor
TPGActorList
TPGActorScene
TPGAIState
TPGAIStateMachine
TPGAIActor
TPGEntity
TPGTimer
TPGDisplayDevice
TPGApplication
TPGAudio
TPGAudioPlayer
TPGList
TPGSparseArray
TPGSQLQueryBuilder
TPGDatabase
TPGDatabaseTable
TPGFont
TPGFontStudio
TPGGame
TPGRenderDevice
TPGTexture
TPGSurface
TPGPolygon
TPGPolyPoint
TPGSprite
TPGImage
TPGBezier
TPGHighscore
TPGInput
TPGIPCServer
TPGIPCClient
TPGMath
TPGNet
TPGPersistentObject
TPGLogFile
TPGConfigFile
TPGStream
TPGMemoryStream
TPGFileStream
TPGExeResourceStream
TPGArchive
TPGUnzipArchive
TPGIniFile
TPGXML
TPGStarfield
TPGTestCase
TPGGraphicalTestCase
TPGTestCaseMenu
TPGStartupDialog
TPGMenu
TPGUtils
TPGDirList
TPGExeMod
TPGPersistence
TPGPlugin
TPGPluginManager[br]Some things I did not yet mention was support for making game plugins, doing test cases, the game framework, the basic menuing system, XML support, configuration file support, various stream types, math routines, directly modifying exe data and much more. ECOM allows it to work with all 32 bit versions of Delphi, C++ Builder 2010. There is even a API layer exposing the major features of the engine allowing easier bindings for other languages. Out of the box comes bindings for Delphi and C++.
Woah thats quite the list. Hope the documentation is really extensive. ;)
chronozphere
11-04-2010, 06:36 AM
Amazing. :) This library will give me idea's on how to design my own engine.
Pyrogine
11-04-2010, 10:52 AM
@WILL
Documentation will be a beast unto itself, sigh. But I'm committed to providing great docs.
@chronozphere
Thanks. 10+ years of continual growth and refinement.
It grow out of a need to make my own projects, a vision for a robust solution that gives you a platform for growth. It's higher level than using DirectX alone, but still low enough that you can build on top.
Also over the years I've been learning about component based development and how it can help with building and managing complex systems. Combine this with some agile methodology and you can streamline your development efforts. Ok, what does all that mean? I've asked myself these questions many, many times. It certainly sounds good, but how can it be applied on a practice level? Well, the idea is that as your software project continues to grow and become more complex, are you able to make changes without the whole thing exploding or you finding that it's just easier to start all over because that would be easier than making the needed changes? Son, I can not tell you how many times I've been in that position. I just started over because what I had was working, but there was no room for growth and to change things became to much of a mess.
The idea is that if you design your project around the notion of components where you have a tree for example that you can hang functionality on, this functionality is more or less an independent logical entity. The TPGActorList can be that tree and the TPGActor object can be that logical entity that can perform some functionality. The ActorList can send messages to actors and actors can respond to those messages. For example, you dropped a nuke and it's time for all actors in a certain range to die, you can broadcast a message that you need to go away because a nuke just went off. In this case they respond by terminating themselves. In cases where you need to send a message and wait for response, the SendMessage function will return the actor that responded or nil if there was no response. My point is that there is a logical way that you can now communicate with all the fruits of functionally hanging on the vine so to speak.
Now if you design things in such a way so that the actors can be independent, yet they can interact with the rest of the design then if something goes wrong where a particular functionality is not needed or need to be replaced, just remove it or change that actor without having to change the whole tree. By rapidly iterating, changing your design in the areas that need change without effecting everything else you can push development forward in a logical and consistent manager. Note that is can be much more complex than what I've outlined but the concept is the same. Your project can be broken up in a logical fashion where you have a tree and you hang functionality on it. You should be able to add/remove/change thing without adversely effecting everything else. Now when change is needed you can replace this logical functionality rather than having to tare down your whole tree and starting over.
In PG the ActorList is the stage or the playground and the Actor is the player. An actor can have children. Again the tree-functionality concept, where you can add functionally to each actor and those children will be updated when the actor's Update method is called and if the children need to render their Render method is called when the parent's render method is called. So very complex design is possible yet be handled in a logical and consistent manager.
@WILL
Documentation will be a beast unto itself, sigh. But I'm committed to providing great docs.
I know that it's not 100% Lazarus or FPC ready yet, but FPC has a great tool called fpcdoc (I think) that will generate html files for your units all you have to do is fill in the description text and details for each...?
AthenaOfDelphi
11-04-2010, 08:27 PM
@WILL
Documentation will be a beast unto itself, sigh. But I'm committed to providing great docs.
I know that it's not 100% Lazarus or FPC ready yet, but FPC has a great tool called fpcdoc (I think) that will generate html files for your units all you have to do is fill in the description text and details for each...?
And there is PasDoc (my personal choice) and DelphiDoc... unfortunately they both have slightly different comment formats. PasDoc also generates a CHM project to make a CHM from the code, and will if you install GraphViz allow you to produce a couple of dependency charts (non-clickable).
Pyrogine
11-04-2010, 09:41 PM
@WILL, @AthenaOfDelphi
Thanks I will check them out.
One I just found is DelphiCodeToDoc (http://dephicodetodoc.sourceforge.net/index.html).
Pyrogine
11-04-2010, 10:06 PM
I played with DelphiCodeToDoc a little and it seems to be pretty cool too. Let me know what you think of it.
AthenaOfDelphi
11-04-2010, 10:20 PM
I played with DelphiCodeToDoc a little and it seems to be pretty cool too. Let me know what you think of it.
Actually I think that was the one I was thinking of (I've bought a new laptop since I tried them, and I preferred PasDoc so I only have that installed now). Of the two, I preferred PasDoc because it had a better range of supported tags and the resulting documentation was, IMO better.
Pyrogine
11-04-2010, 11:05 PM
Ahh ok I see. There is one in fact called DelphiDoc, which I tried, but I was getting AVs. I will check out PasDoc too.
On another note I just updated the PGNet unit with a new class that support network object persistence. I simply derived a new class from PGNet, override the OnRecive event handler to handle processing objects, finally added a new event handler to received the object rather than the data pointer and size. Now you will be able to send live objects across the wire.
Deceleration
TPGNetObjects = class(TPGNet)
protected
FPersistence: TPGPersistence;
procedure OnReceive(const aHost: TPGString; aPort: Integer; aData: Pointer; aSize: Integer);
public
constructor Create; override;
destructor Destroy; override;
procedure OnReceiveObject(const aHost: TPGString; aPort: Integer; aObj: TPGPersistentObject); virtual;
end;
Implementation
procedure TPGNetObjects.OnReceive(const aHost: TPGString; aPort: Integer; aData: Pointer; aSize: Integer);
function GetObject(aData: Pointer; aSize: Integer): TPGPersistentObject;
var
stm: TPGMemoryStream;
begin
Result := nil;
if aData = nil then Exit;
stm := TPGMemoryStream.Create;
try
stm.OpenBuffer(aData, aSize);
Result := FPersistence.LoadObject(stm);
finally
PG_FreeAndNil(stm);
end;
end;
var
obj: TPGPersistentObject;
begin
// check if incoming data has a possibly valid persistant object
// signature
if (aData <> nil) and (aSize > FPersistence.ObjIdSize) then
begin
// try to get object instance from incoming stream
obj := GetObject(aData, aSize);
// if we got a valid object then pass it to the event handler
if Assigned(obj) then
begin
// call event handler
OnReceiveObject(aHost, aPort, obj);
// free object
PG_FreeAndNil(obj);
end;
end;
end;
constructor TPGNetObjects.Create;
begin
inherited;
SetReceiveEvent(OnReceive);
FPersistence := TPGPersistence.Create;
end;
destructor TPGNetObjects.Destroy;
begin
PG_FreeAndNil(FPersistence);
inherited;
end;
procedure TPGNetObjects.OnReceiveObject(const aHost: TPGString; aPort: Integer; aObj: TPGPersistentObject);
begin
// handle received objects here, override for your own derived classes
end;
paul_nicholls
11-04-2010, 11:53 PM
Ahh ok I see. There is one in fact called DelphiDoc, which I tried, but I was getting AVs. I will check out PasDoc too.
On another note I just updated the PGNet unit with a new class that support network object persistence. I simply derived a new class from PGNet, override the OnRecive event handler to handle processing objects, finally added a new event handler to received the object rather than the data pointer and size. Now you will be able to send live objects across the wire.
Deceleration
TPGNetObjects = class(TPGNet)
protected
FPersistence: TPGPersistence;
procedure OnReceive(const aHost: TPGString; aPort: Integer; aData: Pointer; aSize: Integer);
public
constructor Create; override;
destructor Destroy; override;
procedure OnReceiveObject(const aHost: TPGString; aPort: Integer; aObj: TPGPersistentObject); virtual;
end;
Implementation
procedure TPGNetObjects.OnReceive(const aHost: TPGString; aPort: Integer; aData: Pointer; aSize: Integer);
function GetObject(aData: Pointer; aSize: Integer): TPGPersistentObject;
var
stm: TPGMemoryStream;
begin
Result := nil;
if aData = nil then Exit;
stm := TPGMemoryStream.Create;
try
stm.OpenBuffer(aData, aSize);
Result := FPersistence.LoadObject(stm);
finally
PG_FreeAndNil(stm);
end;
end;
var
obj: TPGPersistentObject;
begin
// check if incoming data has a possibly valid persistant object
// signature
if (aData <> nil) and (aSize > FPersistence.ObjIdSize) then
begin
// try to get object instance from incoming stream
obj := GetObject(aData, aSize);
// if we got a valid object then pass it to the event handler
if Assigned(obj) then
begin
// call event handler
OnReceiveObject(aHost, aPort, obj);
// free object
PG_FreeAndNil(obj);
end;
end;
end;
constructor TPGNetObjects.Create;
begin
inherited;
SetReceiveEvent(OnReceive);
FPersistence := TPGPersistence.Create;
end;
destructor TPGNetObjects.Destroy;
begin
PG_FreeAndNil(FPersistence);
inherited;
end;
procedure TPGNetObjects.OnReceiveObject(const aHost: TPGString; aPort: Integer; aObj: TPGPersistentObject);
begin
// handle received objects here, override for your own derived classes
end;
Very nice :)
cheers,
Paul
Pyrogine
20-04-2010, 12:08 PM
I've released v0.4.0 developer version. The developer version is the current work-in-progress daily build that will eventually become the next stable release version.
Changelog
(4/19/2010):
Added command-line PyroGine Pascal Compiler (pgpcc.exe)
Added embeddable script compiler (TPGScript, Pascal, Basic and JavaScript syntax)
Added support for loading and using DLLs stored in an EXE as RC_Data (PG.ResourceLoadLibrary, PG.ResourceFreeLibrary, PG.ResourceBindToPro).
Also, join me on Facebook (http://www.facebook.com/pyroginedev) and Twitter (http://twitter.com/pyroginedev).
paul_nicholls
22-04-2010, 12:21 AM
Nice work Jarrod :)
OT:
I am so angry with myself >:(
I have had nothing but trouble today...
1. I got onto the bus to go to work, pulled out my mp3 player, and found it was all scratched up - I hadn't put it back into it's protective sleeve, and stuff in the front pocket of my bag scratched the front of it - totally my own fault, but D'OH!.
2. I got to work, pulled out my 8GB USB key, put it into the computer, and tried to access some stuff from it. The computer politely informs me that it isn't formatted, and do I want to do this now...
It was fine yesterday, but it now looks like I have lost everything that was on it, including my PyroGine project "The Probe"! Again, my fault for not backing it up onto the hard-drive, but still...AAARRRRRGGGGGHHHH!!! I do have some older code for the game elsewhere, but I have lost the shiny new code I wrote in the last couple of days when re-factoring the game...
I wonder what ELSE will go wrong today (seem to come in threes?) :(
cheers,
Paul
chronozphere
22-04-2010, 09:25 AM
That's a bummer man! :(
Fortunately, It takes less time to re-write that code, because you probably remember what stuff you added/changed. Just do that ASAP, so you don't forget. Also, harddrives are alot safer for storage. I have 2 USB-sticks but I don't use them that often (I use google docs, SVN and SSH (on linux) alot, so I dont have to carry my stuff with me).
paul_nicholls
22-04-2010, 11:23 AM
That's a bummer man! :(
Fortunately, It takes less time to re-write that code, because you probably remember what stuff you added/changed. Just do that ASAP, so you don't forget. Also, harddrives are alot safer for storage. I have 2 USB-sticks but I don't use them that often (I use google docs, SVN and SSH (on linux) alot, so I dont have to carry my stuff with me).
yeah, I'd better re-do it ASAP so I remember it as best as I can. Mind you, it wasn't working completely so I might rewrite it anyway ;)
cheers,
Paul
Pyrogine
22-04-2010, 11:48 AM
Sigh. I know this all too well myself. Now my main dev box is mirrored to my external drive and backed up off site. A smaller external drive gave up the ghost recently and there where very important files on it that I had failed to copy off.... yea... one of those &^%$#@#% moments. I found this program called ZAR (Zero Assumption Recovery) that allowed me to successfully recover all my data. Phew! From what I understand it will work with USB drives too. Paul I hope you're able to recover your data.
On a different note... a new daily update to PyroGine Game Engine v0.4.0 developer version has been posted.
http://pyrogine.com/media/sa,media/in,9/thumb/ (http://pyrogine.com/media/sa,item/in,9/)
Added light-weight IDE for PyroGine Pascal Compiler
Added PyroGine Pascal Examples
Now out of the box you have:
Delphi 5+ higher support (full oop)
C++ Builder 2010 support (full oop)
Standard C++ support (api)
Embeddable scripting (Pascal, Basic, JavaScript)
Object Pascal + standalone IDE
Traveler
24-04-2010, 01:34 PM
Hi Jarrod,
I've been looking into PyroGine for the Ludum Dare competition. I have some trouble getting the demos to compile though. I use Delphi 7 and many of the Pascal demos include this line {$RCDATA uOptionsForm.dfm} (in unit uOptionsForm). Delphi unfortunately comes up with an invalid compiler directive: RCData error. Is this because the demos in the pascal directory are actually for Freepascal and not Delphi?
I remember having seen the next question before (I think Paul asked it too) but I cant seem to find it anymore. How do I make text visible? I see things like 'PG.Font.Draw(3,3, 1.0, PG_White, rsImage, fjLeft, '%d fps', [PG.Timer.Framerate]);' in the sourcecode, yet none of the demos actually display any of the texts.
Thanks,
Pyrogine
24-04-2010, 02:08 PM
Hi,
All the sources in the pascal folder are meant to be compiled with the commandline compiler/ide. There are/will be extensions that are not available in Delphi/Freepascal. The RCDATA directive for example allows you to easily embed RT_DATA into your EXE/DLM file. This is currently used to bind .dfm data inside to exe for use with forms. RCDATA will be an extended form of the normal $R compiler directive. All the code in the delphi folder will/should compile in Delphi 5 and higher.
PG is a global application object like the global application object in Delphi. It holds singleton objects such as the display device, render device and for convenience a default proportionally spaced console font which can be used for debugging. To load new fonts you can create an instance of TPGFontSTudio, load and render your fonts. Since fonts are textured objects, the DrawXXX methods must be used between RenderDevice.StartFrame and RenderDevice.Endframe methods.
Make sure you have the latest dev build (v0.4.0) and load up the delTestbed project in D7 and it should compile and run. You have to add the sdk folder to the Delphi path so those files can be referenced. See uRenderDevice.pas in examples/delphi to see how to setup a minimal render loop that will simply display the frame rate.
So, examples\delphi should be used by native Delphi , examples\pascal should be used by pgpcc/pgpdev. You can use pgpdev to make scripts/dlms for access from your native code if you wish or use it to make the whole app since it can generate EXEs. PyroGine.dll still has be present along side the EXE however.
Let me know.
Pyrogine
24-04-2010, 02:12 PM
Also in most of the examples you wont see StartFrame/EndFrame because they are using the TPGTestCase framework, which is just a layer around the basic code that is in uRenderDevice which takes care of getting everything setup for you so you can quickly test out your ideas. Using TPGTestCase you just override the proper methods. I included uDisplayDevice.pas and uRenderDevice.pas to show how to properly setup a basic render loop if you want to work at the lowest level.
Traveler
24-04-2010, 02:36 PM
Thanks for the quick replies :)
I have done as you said and downloaded the latest build. However, none of the Delphi samples show any text. The only thing uRenderDevice.pas file appears to do is display a blue screen, but no fps text.
Pyrogine
24-04-2010, 04:43 PM
Hmmm... interesting.
Tell me:
What vid card are you using? Using the latest drivers?
What version of DirectX are you using? Needs DX9 Summer 2003 run-time or higher
Does you're card supports 3D in a window? Run delTestBed when the startup dialog shows, click options and change to fullscreen. Do they show up in fullscreen?
PG only use 32bit textures internally, but if the texture can not be created it will add an entry to the log file and abort.
See if there is a *.log file in the same folder with the exe (should be exename.log). See if there are any errors.
If your running from VirtualBox you have to make sure the Direct3D support is installed which translate to ogl calls so you have to have good ogl drivers in this case.
Make sure the PyroGine dll in the distro is the correct one being called. For example if you've used an older version where the DLLs where in the examples folder too and you've unzipped the newer version over the same folder, the exes will be calling the wrong version. Starting with v0.4.0, the PG DLL is only in the 1.0/bin folder
Traveler
24-04-2010, 07:34 PM
I have tested this on two machines. One i7, Windows 7, Radeon 4870. One Core2 Duo laptop using XP and a crappy Mobile Intel 4 series 3d card. Both appear to have no problems running the demos.
I have retested the Delphi demos (using delTestbed.exe) but none of them show any text at all (except for the line that says it made with the Pyrogine game engine.) AstroBlaster, PollyPointcollision and Elastic seem capable of running fullscreen, while RenderDevice and DisplayDevice aren't.
There are no log files.
I have made sure that the dll file is in the same directory as the executable.
I have also tested the demos inside the examples\bin directory. AstroBlaster does have text there (though it's of a very low quality) but RenderDevice still has nothing. Interestingly I can only run the demos using delTestBed.exe or scrTestbed.exe. All other exe's either crash after shutdown or on startup(pasTestbed).
One other thing I noticed while toying with the source code. If I make an error that only occurs during runtime I'm not presented with an error. The game just closes, this makes debugging very hard because I have no clue where to look. Is there a way around that?
Pyrogine
24-04-2010, 09:11 PM
Hi,
Download this test file (http://pyrogine.com/support/Traveler/Traveler.zip), unzip and compile with Delphi. You should get something that looks like this:
http://pyrogine.com/support/Traveler/test1.png (http://pyrogine.com/support/Traveler/test1.png)
The code will load a native FontStudio font file and draw using a few of the render methods. This demo is using a larger font along with the rsBlend render state. I suspect that texture coords are too small for the default console font which has to use the rsImage render state in order to see it because it's very small and does not support blending. For font you will either use rsBlend or rsImage, blend will give you the best overall quality.
program test1;
{$APPTYPE CONSOLE}
uses
SysUtils,
PGGraphics,
PGFonts,
PGApplication;
var
Font: TPGFontStudio;
begin
PG.DisplayDevice.Open('Traveler - Test1', dm800x600, True, True);
PG.RenderDevice.SetMode(0, seDiscard);
Font := TPGFontStudio.Create;
Font.LoadFromFile('Impact19.fnt', PG_FontColorKey);
while not PG.Terminated do
begin
PG.ProcessMessages;
if not PG.RenderDevice.Ready then
begin
Sleep(55);
continue;
end;
PG.RenderDevice.ClearFrame(cfDefault, PG_SkyBlue);
if PG.RenderDevice.StartFrame then
begin
PG.Font.Draw(3, 3, 1.0, PG_White, rsImage, fjCenter, 'Testing a few font features using font Impact 19px', []);
PG.Font.Draw(3, 20, 1.0, PG_White, rsImage, fjCenter, 'Created with the FontStudio utility.', []);
Font.Draw(0,100,1.0,PG_Red,rsBlend,fjLeft, 'Left', []);
Font.Draw(0,120,1.0,PG_White,rsBlend,fjRight, 'Right', []);
Font.Draw(0,140,1.0,PG_Blue,rsBlend,fjCenter, 'Center', []);
Font.DrawGradient(0,180,1.0, PG_Green, PG_Blue, rsBlend, fjCenter, 'Gradient', []);
Font.DrawGradient(0,200,3.0, PG_Red, PG_Blue, rsBlend, fjCenter, 'Scaled', []);
PG.RenderDevice.EndFrame;
end;
PG.RenderDevice.ShowFrame;
end;
FreeAndNil(Font);
PG.RenderDevice.RestoreMode;
PG.DisplayDevice.Close;
end.
Traveler
24-04-2010, 09:38 PM
Excellent! That works like a charm :)
I see now where the confusion came from. I was under the impression that text was being written to the screen, without the use of files generated by Font Studio.
I have one more query, I hope you don't mind. I have gone through the entire AstroBlaster source and even duplicated the demo part by part. Thing is, my version refuses to listen to keyboard input. I assume I have overlooked something, but I cant find what that is. Do you perhaps have an idea?
Thanks again.
Pyrogine
24-04-2010, 10:28 PM
Ahh, good to hear it's working for you.
Yea everything that you see on screen other than low-level pixel routines are drawn using textures and PG support FS native textured fonts. Note that the TPGFont class is the base class for fonts in PG so if you have a need to support another font utility or wish to create your own textured fonts you can do so.
If your demo is derived from TPGTestCase or TPGGraphicalTestCase then PG.Input.Update is being called for you when your Update method is called, but you have to make sure you call inherited so that the inherited code gets called. If you're not using TestCase, then you must call PG.Input.Update manually each frame.
TestCase based code:
procedure TYouDemo.UpdateFrame(aElapsedTime: Single);
begin
// must call inherited so that inherited code which automatically calls Input.Update
inherited;
// your code here
...
end;
This applies to LoadResources/FreeResources methods as well. For example when inherited is called inside LoadResources, the display and render devices are setup for you.
if you using a raw game loop you have to make sure you manually open, process and close input:
program test1;
{$APPTYPE CONSOLE}
uses
SysUtils,
PGGraphics,
PGFonts,
PGApplication;
var
Font: TPGFontStudio;
begin
PG.DisplayDevice.Open('Traveler - Test1', dm800x600, True, True);
PG.RenderDevice.SetMode(0, seDiscard);
Font := TPGFontStudio.Create;
Font.LoadFromFile('Impact19.fnt', PG_FontColorKey);
// open input using the open DisplayDevice's window handle
PG.Input.Open;
while not PG.Terminated do
begin
PG.ProcessMessages;
if not PG.RenderDevice.Ready then
begin
Sleep(55);
continue;
end;
// this area is where you do updating
// process input
PG.Input.Update;
// you can check for keys, update simulation etc.
if PG.Input.KeyHit(KEY_ESCAPE) then
begin
PG.Terminated := True;
end;
PG.RenderDevice.ClearFrame(cfDefault, PG_SkyBlue);
if PG.RenderDevice.StartFrame then
begin
PG.Font.Draw(3, 3, 1.0, PG_White, rsImage, fjCenter, 'Testing a few font features using font Impact 19px', []);
PG.Font.Draw(3, 20, 1.0, PG_White, rsImage, fjCenter, 'Created with the FontStudio utility.', []);
Font.Draw(0,100,1.0,PG_Red,rsBlend,fjLeft, 'Left', []);
Font.Draw(0,120,1.0,PG_White,rsBlend,fjRight, 'Right', []);
Font.Draw(0,140,1.0,PG_Blue,rsBlend,fjCenter, 'Center', []);
Font.DrawGradient(0,180,1.0, PG_Green, PG_Blue, rsBlend, fjCenter, 'Gradient', []);
Font.DrawGradient(0,200,3.0, PG_Red, PG_Blue, rsBlend, fjCenter, 'Scaled', []);
PG.RenderDevice.EndFrame;
end;
PG.RenderDevice.ShowFrame;
end;
// close input
PG.Input.Close;
FreeAndNil(Font);
PG.RenderDevice.RestoreMode;
PG.DisplayDevice.Close;
end.
No worries. I'm here to help so feel free to ask away. It helps to find and quash any bugs and we can get a better understand how PG works and different ways it can be used. So thank you as well.
Traveler
24-04-2010, 10:45 PM
Okay, that makes perfect sense.
My demo is derived from TPGGame. I used Game.dpr in the Pascal directory as a base template. I tried to manually add PG.Input.Update in UpdateFrame(aElapsedTime: Single), but that does not seem to do much.
What is the preferred class to work with? TPGTestCase or TPGGraphicalTestCase somehow seemed 'wrong' to me to use as they implied test classes used specifically for the Delphi demos.
Pyrogine
24-04-2010, 11:05 PM
Actually, if you're using TPGame, which is what you want to use for working code, then that should be fine. However, you do have to Open input before it will work. You can open it in two ways. Via PG.Input.Open, which will use the handle from PG.DisplayDevice, which has to already be open or if you're using your own window such as a form for example you need to use PG.Input.OpenEx which allows you to pass in a window handle.
TPGTestCase and TPGGraphicalTestCase should be use for quick prototyping as a lot of stuff will already be setup for you which allows to try out your ideas and concepts with worrying about the plumbing. Your production code should use TPGGame.
Doing this:
type
TGame = class(TPGame);
var
g: TGame;
...
g := TGame.Create;
g.Run;
g.Free
is enough to get the SkyBlue screen. To do more you should override the necessary routines such as LoadResources/FreeResources, Init/Done etc. For Game I use Init/Done for set up stuff that is global such as the archive file, configuration and so forth. LoadResources/FreeResources should be use to init your textures, audio, open input etc.
If you look at uGame.pas in examples/pascal (which your using), this is the basic template need for using TPGame. If you use pggdev and create a new Game project this template will be generated automatically for you (same for a test case project).
So for your class derived from TPGame I think all you needed was to make sure you open input then it should work. I would use uGAme.pas as a template because these are all the methods that you need to override to take advantage of it. Again, note that for testcase, most of the important stuff will be setup/shutdown for you, in game you have to do it all manually. Let me know if that worked. I can post a demo based on TPGame to make it more clear if you want, let me know.
Traveler
24-04-2010, 11:27 PM
Most excellent! Input is working now. Thanks again for all the quick and helpful replies. :)
Pyrogine
24-04-2010, 11:49 PM
Welcome.
Oh, about the run-time errors.... if they can be captured there should be a .log file generated. For example if you try and load a texture and it is not successful, the reasons will (should) be posted in the .log file.
Pyrogine
26-04-2010, 12:49 PM
PyroGine v0.4.0 Dev Update (04/26/2010)
Tweaked PolyPoint Collision code. (Thanks Traveler)
TPGStartupDialog was returning the wrong state when clicking on the dialog close button. (Thanks Traveler)
Generated EXE image from TPGScript and PGPCC are now fully compatible with each other.
paul_nicholls
12-05-2010, 06:37 AM
Hi Jarrod, I have a Pyrogine question - how on earth do I tell the engine to quit/end the application?
Perhaps I am completely blind, but I can only find the Abort() method which I am using when I press Escape ATM...
cheers,
Paul
Pyrogine
12-05-2010, 09:42 AM
Hi,
Set PG.Terminated to True.
paul_nicholls
12-05-2010, 09:47 AM
Hi,
Set PG.Terminated to True.
AHH! thanks chief :)
cheers,
Paul
Pyrogine
12-05-2010, 02:12 PM
Yea, when it's True then it will fall out of the game loop and terminate.
Only use Abort when the application must end at that moment. When executed the exception handlers will be called so be prepared to handle shut for this case. For example, a resource can not be loaded thus the app can not continue, you can abort. Either free any allocated resources before hand or make sure they are handled during the exception call. The message will be sent to the log file as well as for any unhandled exception at run-time.
Pyrogine
16-05-2010, 10:35 PM
New build that should fix reported problems. See this post (http://www.pascalgamedevelopment.com/forum/index.php?topic=6264.msg50846#msg50846).
Pyrogine
18-05-2010, 02:11 AM
PyroGine Development (http://pyrogine.com/index.php?page=about_us) has released PyroGine Game Engine (http://pyrogine.com/index.php?page=pyrogine) v0.4.3:
Changelog
Added contrib folder containing PGE community member contributions.
Fixed the embedded scripting engine crashing when registering a class in an Ansi version of Delphi (Thanks pstudio).
Added rsMonoLightMap and rsColorLightMap render states to support textured light mapping.
pstudio
19-05-2010, 02:14 AM
Hi again Jarrod,
I'm working on a small demo project and right now I'm trying to get my head around the Actor System in PGE. I have some questions I hope you will answer.
My first question is about multiple animations for an entity. Is the idea to have all the animations saved in the same group and then use SetFrameRange to change animations? First I assumed you would save each animation in its own group but that doesn't seem right.
Is there a collision body in an Actor? In an Entity there is the PolyPoint which I assume defines the body but I'm not sure if there is any in an Actor. Can I make my own class inheriting from the Actor class, and then check collisions between that class and Entities? Generally I would like to know more about the collision system in PGE.
I'm wondering what happens when I call Render from an ActorScene? Will all Actors Render method be called or is there some sort of occlusion culling going on behind the scene? Should I make sure myself that only the right Actors are drawn?
Am I right in assuming that an Entities position is a screen position? It simply tells where to render the Entity on screen and then I'll have to manage myself were it would be in a game world?
What does the Is(Fully)Visible functions exactly indicate in Entity?
Does the third component in vector aHitPos mean anything in OnCollide? Angle? Nothing?
That is quite a few questions but I would like to get a good understanding of how the Actor system works before I start using it for real. I don't want to make some design decisions early on only to find out later that I can't do it that way.
Pyrogine
19-05-2010, 03:51 AM
Hi,
TPGSprite allow you to organize your images into pages and groups. A page defines the texture where the images are located and groups are the logical representation of these images. They can be ordered list to form animation or an unordered list just to represent lists of images that you need to reference. An entity represents an actor that can have shape, animation and position in the game world. They can be scaled and do collision. All the animation for an entity can be in the group and you have control over what is played back with the frame methods. Note that TPGSprite is a manager and you do not have to have multiple sprite instances. One sprite object is capable of managing all the images for your project. You can however create as many instances of TPGSprite as you would need.
TPGActor represents the base level of any object that needs to logically exits in the game world. Think of it as assembly language and an entity as Pascal or C++. The event handlers for collision are there but you would need to implement them yourself. Entity does this for you giving you both spherical and as well as polypoint collision detection.
When you can ActorScene Update/Render it will call the standard actor event handlers OnRender and OnUpdate. They do nothing by default. TPGActor is abstract so you can update and render what ever definition that represent your derived actor class.
Yes, an entity exist in screen space which will be the current view port by default. However if you need to represent a virtual space you can pass in a VirtualX and VirtualY to the render method which will allow you to represent your entity in virtual space. This will align the entity to view port location. Otherwise you would pass zero for these values.
IsVisible will be try if part of the entity is on screen and IsFullyVisible will be true if the entity is fully on screen.
Currently the z component is not used for anything concerning the entity and you may use it for your own purposes. When you call the GetMouseXXX methods the value for the mouse wheel will be returned in the z component.
AIActor and AIEntity are extended versions that includes a state machine. The OnUpdate and OnRender methods are overridden so that they run the update and render methods of the AIStates. This allows your actor/entity to be driven by the event driven finite state machine that is available in PGE.
An Actor is an abstract representation of a logical body in the PGE game world. An Entity is an actor that has shape, position, etc. The actor allows you to represent your own entity type in the game world. If you want to do polypoint collision for your own actors you can plugin in the TPGPolyPoint object for this. PGE is high level, yet low enough where you can do must of what you need to do.
Pyrogine
03-06-2010, 06:45 PM
PyroGine Development has released version 0.4.4 of PyroGine Game Engine. Some important bug fixes and enhancements have been made in this release and we encourage everyone to upgrade to this latest version.
Changelog:
Renamed include file GLOBALDEFS.INC to PGEDEFINES.INC to be more consistent with the PGE naming convention.
Fixed a problem where the wrong interface was being returned when creating a TPGPolygon object.
Update PGETestbed options dialog to allow picking a suitable resolution from a list of enumerated modes.
Added TPGRenderDevice.FindMatchingAdapterMode. (Thanks Paul Nicholls)
Added methods to TPGRenderDevice: AdapterCount, AdapterAspectRatio, AdapterModeCount, AdapterModes and EnumAdapterModes to enumerate and return adapter display modes.
Added TPGAudio.ChannelsPlaying which returns the number of playing channels.
Fixed a 1-off bug in TPGTexture.Render. (Thanks Paul Nicholls)
Fixed enum size mismatch problem.
Added TPGRenderDevice.DrawTexturedLine for drawing textured lines.
Added a width parameter to the TPGRenderDevice.DrawLine method.
Added the DirectX9 headers to the contrib folder. (Thanks Paul Nicholls)
Removed C/C++ support for now, it may return in the future.
Removed pgpcc and pgpdev. We may rework them as a stand alone product in the future.
Added TPGDisplayDevice.GetDesktopSize to turn the desktop width and height. (Thanks Paul Nicholls)
Fixed a 1-off bug in TPGSprite.AddImageFromGrid. (Thanks Paul Nicholls)
paul_nicholls
21-06-2010, 01:13 AM
Hi Jarrod,
just how often does the TPGGraphicalTestCase.UpdateFrame actually get called??
I have had trouble with "The Probe" in getting animation timings correct as it seems to be called much more than once per frame :(
At first I thought it was just me getting timings incorrect, but now it really shows since I've just added a time count of how long a level has lasted (hours:minutes:seconds).
I have this code that gets called each UpdateFrame call, but in order to make the seconds acually be close to seconds and not super quick, I need to compare the FTimeCount accumulation against 30 instead of the expected 1 (for a single second)!!!
This is rather annoying!! :(
FTimeCount := FTimeCount + aElapsedTime;
if FTimeCount >= 30 then begin //<-- here I would have expected 1 instead of 30!!
FTimeCount := 0;
Inc(FTime.Seconds);
if FTime.Seconds >= 60 then begin
FTime.Seconds := 0;
Inc(FTime.Minutes);
if FTime.Minutes >= 60 then begin
FTime.Minutes := 0;
Inc(FTime.Hours);
end;
end;
end;
Any ideas?
Oh, I am definitely using the latest Pyrogine code + dlls too...
cheers,
Paul
Pyrogine
21-06-2010, 01:59 AM
Hi,
It only gets called once per frame as do any OnUpdate and OnRender methods. The problem your having is because aElapsedTime does not represent the value that you're expecting. The TPGTimer class in PGE uses frame-based timing. If you call TPGTimer.Init(35.05, 2), this tells the timer that you want your simulation to run at 35 fps and to try and maintain this rate if the rate drops by two times. So then what does aElapsedTime represent? If the current frame is running at 17.5 fps, in order to keep the simulation running at 35 fps, aElapsedTime would be a value of 1.5 so when you multiply your object speed by aElapsedTime then it will speed the objects up to keep the overall simulation at 35 fps. If the frame rate go to 60+ fps then aElapsedTime would be around 0.5. It will continue to try to do this if the frame drops/increases by two frames.
The TPTimer class (PG.Timer) already has methods to help you with the timing your trying to do. You can use the TPTimer.FrameSpeed method. You use it like this:
var
timer: Single;
...
timer := 0;
...
// return TRUE if 30 fps has elapsed
if PG.FrameSpeed(timer, 30.0) then
begin
...
end;
The timer variable is used so that you track different timing speeds if needed. So, when doing your timing, keep in mind that aElapsedTime will return a value to represents a quantity that will keep objects in your simulation running at the specified desired simulation rate. By default TPTimer will set to 35 fps. This is a historical value that I've used the past 8+ years. In general I've found that optimal and silky smooth animation is better if your simulation rate is multiple of your monitors refresh rate. 60Hhz is a standard refresh rate for all monitors and it tends to be the default. So running at slightly above half this rate has proven to produce good results. You can of coarse change the simulation timing to what ever value you choose, but keep in mind the things I've outlined here.
So in this case your simulation is running a 35 fps. What you should do is this:
var
FTimeCount: Single;
FSecs: Integer;
...
// update counter
FTimeCount := FTimeCount + (1.0 * aElapsedTime);
// check if a second has passed
if FTimeCount >= PG.Timer.GetDesiredFPS then
begin
FTimeCount := FTimeCount - PG.Timer.GetDesiredFPS;
Inc(FSecs);
end
paul_nicholls
21-06-2010, 02:14 AM
Hi Jarrod, thanks for the quick reply! :)
So you are saying that PG.DesiredFPS is equivalent to 1 second in sumulation time then?
So if I wanted something to trigger at 1/2 second for example, I would use PG.DesiredFPS / 2?
cool, I will give it a go..thanks dude :)
cheers,
Paul
Pyrogine
21-06-2010, 02:21 AM
Im saying that PG.Timer.DesiredFPS is the value that you set when PG.Timer.Init was called. Since aElapsedTime will always be a quantity associated with DesiredFPS then if you multiple aElapsedTime by 1.0 it will normalize it one second in this case. When the accumulated value become greater than PG.Timer.DesiredFPS (35) then one second of time has passed. If you wanted to check for 1/2 second then yes it would be 1/2 that (17.5).
paul_nicholls
21-06-2010, 02:35 AM
Im saying that PG.Timer.DesiredFPS is the value that you set when PG.Timer.Init was called. Since aElapsedTime will always be a quantity associated with DesiredFPS then if you multiple aElapsedTime by 1.0 it will normalize it one second in this case. When the accumulated value become greater than PG.Timer.DesiredFPS (35) then one second of time has passed. If you wanted to check for 1/2 second then yes it would be 1/2 that (17.5).
Ok, I think I understand :)
Expanding on this, I have a bunch of other objects in my game that I want to control using the elapsed frame time in seconds (or fractions thereof) by passing in this time into their .Update() method...
To pass in the number of seconds into these methods, do I do this kind of thing?
var
TimeSlice: Single;
begin
TimeSlice := 1.0 * aElapsedTime / PG.Timer.DesiredFPS;
...
MyObject.Update(TimeSlice);
I'm hoping this will pass in a time I can then compare to, let's say 1 = 1 second, 0.010 for 10ms?
EDIT: just so you know, the fix for my global game time now works lovely thanks to you :)
FTimeCount := FTimeCount + (1.0 * aElapsedTime);
if FTimeCount >= PG.Timer.GetDesiredFPS then begin
FTimeCount := 0;
Inc(FTime.Seconds);
if FTime.Seconds >= 60 then begin
FTime.Seconds := 0;
Inc(FTime.Minutes);
if FTime.Minutes >= 60 then begin
FTime.Minutes := 0;
Inc(FTime.Hours);
end;
end;
end;
cheers,
Paul
paul_nicholls
21-06-2010, 02:47 AM
Ok, this seems to work for me just fine, yay!
var
TimeSlice: Single;
begin
TimeSlice := 1.0 * aElapsedTime / PG.Timer.DesiredFPS;
...
MyObject.Update(TimeSlice);
cheers,
Paul
Pyrogine
21-06-2010, 03:11 AM
All you objects should be updated based on aElapsedTime. The overall effect is that your simulation will be be updated at a know and predictable rate while the game loop can update as fast as the host machine can process the loop. If you you need to do timing based on a specific fps independent of the current simulation rate then just use TPGTimer.FrameSpeed which allows you to specify a specific fps independent of the simulation rate. What your doing is decoupling rendering from physics. The extra cycle per frame on faster machines will have no notable effects on your simulation while generating smoother graphics and more cycle can be devoted to AI and other processing. On slower machines, if you keep simulation a magnitude lower than the refresh rate of your monitor, then the frame can drop without any noticeable effects.
Any way if what your trying to do is working then keep going, but keep in mind what I've outlined as the proper way of using frame-based timing in PGE.
paul_nicholls
21-06-2010, 03:23 AM
Thanks chief :)
cheers,
Paul
Powered by vBulletin® Version 4.2.5 Copyright © 2024 vBulletin Solutions Inc. All rights reserved.