PDA

View Full Version : Space Shooter Game Editor



robert83
28-12-2012, 07:47 PM
Hello Everyone ,

I'm in the process of creating a map editor, but ran into a problem, I'm using tiles 32x32 , my widht is fixed 20x32 (640) , but I can dynamicaly set the length, if I generate say a map that is 1500x32 tiles long that , my editor slows down to a crawl, probably because I'm rendering all the sprites at once, not just the ones visible . How can I render only the visible sprites?

Attached I have here my entire project. Still lots of stuff to do but I think I'm progressing nicely. Anyway try to generate a map that is say 150 in length, you'll see the FPS drops drasticaly...
Can someone please help me with this? Only draw what is actually visible.

Greetings
Robert

SilverWarior
28-12-2012, 10:02 PM
Unfrtunately I havent been able to compile your project as I don't have all components so I can't offer you exact code changes: But becouse I was doing something similar once I can still give you some hints:

1. Instead of creating all 1500x32 sprites create just as many sprites as it fits your window. Then you are only changing the sprites texture (Image).
For instacne you define your map the same way as you do using:

TMap: array of array of Integer;
Then you define multidimensional array of sprites:

TSprites: array of array of TSprite;
And then when you move your map you just check which texture should be used for which Sprite like so:

Sprites[X,Y].Texture := Map[X+Left,Y+Top];

2. Decrease the number of redraws as much as posible. Since you are making a map editor you probably don't even need 30 FPS. So increase the timers interval. Or even better if you are only using static Sprites (no animations) you can forget about constant redraws and actually fire redraws only when there has been any change. This can especially come useful when you are forced to render lots of Sprites (zoomed out view of your map for instance).

NOTE: All the code examples are purely out of my head so there is great posibility that they have syntax problems in them.

robert83
29-12-2012, 03:33 PM
THANK YOU VERY VERY MUCH!!! I did it, after spending my day here in front of the computer, checkin DraculLin-s Diablo demo, and your advice, I did it. I'm using a 2d map array to store my map, and using a second array of sprites that is
only as much as needed to fill my rendering surface 640x480 that is 20x15 of 32x32 tiles .

I'm attaching the code for review, please note the Layer buttons don't do anything at all...neither does the delete... a few of them edits are for debuging .... gona clean up the code, and read layers, then I'll play with using Sprites on this map
that actualy have animation , like water , lava ... or grass that moves....

Greetings
Robert

ps.: if you try it, please post FPS , on my machine it has 1100 FPS , Core 2 Duo 7500 , Geforce 250GTS , wonder what it does on slower rig....

SilverWarior
29-12-2012, 04:43 PM
On my laptop (Acer Aspire 6530G with AMD Turion X2 Dual Core 2.0 GHz, ATI Mobility Radeon HD 3650 512 MB VRAM) and Windows 7 I'm getting between 310 and 320 FPS when idle and between 280 and 290 FPS if I do lots of mouse movments which cause selection sprite to move to other tiles.
The performance isn't affected by the size of the map so you did it.

BTW you can use any visual component as Aspyhre renderning target. So you don't actually need to create a new form for renderning area but simply use TPanel component for instance. Offcourse having dedicated form is advised if you are making FullScreen mode game to gain aditional performance.
The reason why I'm telling you this is becouse currently when I launch your program both form sizes exceed the size of my screen so I can't see whole program of yours.

robert83
29-12-2012, 04:57 PM
Thank you very much for the reply. Wow that is great, but how do I do that ? How do I specify AsphyreDevice1 rendring target? I've tried after ApshyreDevice1.Initialize; to add this , AsphyreDevice1.Create(Panel1); but nothing happens...

Greetings
Robert

SilverWarior
29-12-2012, 08:10 PM
To be honest I don't know exactly as newest version of Asphyre had some changes in it. But based on the dfm file of your main form AsphyreDevice1 component which has been placed on your MainForm has property WindowHandle which is currently set to 0.
I belive that changing this property to Panel.Handle should set Panel as rendering target for Asphyre but I'm not sure. You need to change this property prior the Initialization of the engine.

robert83
30-12-2012, 12:52 PM
Did some aditional progress meanwhile, ditched the combobox in favor of ListView, now I can display all them Tiles with Names besides them in a nice manner.

Also added animated background tiles instead of static.... check out water water_1_1_ani.image,water_1_2_ani.image,water_2_1_ ani.image,water_2_2_ani.image , they are supposed to be assembled in a 64x64 tile...
like

Water1,1 ; Water 1,2
Water2,1 ; Water 2,2

And damn they move but look darn bad (did it myself... dang, I'm going to have to look for another nicer....way to animate water , that does not overwhelm me, this requires a far better artist then I.

Anyway please check my latest and greates editor....

Note : it was designed with 1920x1080 resoltion.

Greetings
Robert

SilverWarior
30-12-2012, 02:50 PM
A few more advices:
1. I see that you are treating every sprite as animation sprite now. This is OK but you don't need to have active animation for every sprite especially if it contains just normal picture and not animatio. Having active animation for sprite which contains just an image slows down everything becouse SpriteEngine still treats it as animation with 1 picture and since you are looping the animation it is always updating its image with the same image. So I recomend you to have active animation only for those sprites which are actually animated.

2. In DrawLevel procedure you are calling this:

Used_Tile := Images.Item[map.data[i,j+Offset]];
BackGround[i, j].ImageName:= Used_Tile.Name;
This can slow down everything quite a bit since you are searching for needed images using and Image name which can be quite slow.
Why don't you search for the right image using its index.
Doesn't TAnimatedSprite alows you to specify its image by directly specifying TAsphyreImage? If it does don't search for proper image using its name but simply specify its Index which should be the same Integer value as you are saving it in Map data.

//Note writen from my head
BackGround[i, j].Image = Images.Item[map.data[i,j+Offset]];

3. As for moving water animation the main problem is that you have those white lines which stands out to much. If they would be dark blue it would seen much similar to water than it does now. Infact now it loks more like a bunch of Blue bugs crowding in some place. :D
Several picture editing programs low adding watery effect to the pictures so I suggest for you to check it ot if this is supported by your Picture editing programs and the use it. What you actually want is some constant waves movment.

robert83
31-12-2012, 02:49 PM
Hi,

thank you for the great tips, unfortunately this did not work



BackGround[i, j].ImageIndex = Images.Item[map.data[i,j+Offset]];


but I'll probably have to spend a bit more time with it, tried it once... but since I was thinking about my water tiles, and then saving loading... , I'll return to this later.

Also I was wondering if I could solve my sprite animation possible problems later like this ?



procedure TMain.LoadLevel;
var i,j : integer;
begin
for j := 0 to 14 do
begin
for i := 0 to 19 do
begin
BackGround[i,j] := TBackGround.Create(SpriteEngine);
BackGround[i,j].ImageName:= 'Empty.image';
BackGround[i,j].X:=i*32;
BackGround[i,j].Y:=j*32;
BackGround[i,j].Z:=1;
if BackGround[i,j].PatternCount > 1 then
begin
BackGround[i,j].AnimPos:=random(3);
BackGround[i,j].AnimCount:=3;
BackGround[i,j].AnimSpeed:=0.010;
BackGround[i,j].AnimLooped:=true;
BackGround[i,j].DoAnimate:=true;
end;
end;
end;


Tried it with my current code, but did not work, nothing moved anymore, maybe it's because I've loaded my images incorrectly into my asdb file? (Will have to check later).

Anyway here is the latest greates version, now I think my water tile looks like water....added some other tiles. The program is a mess, I know, but I will do the fine tuning later...

In order to load my test level , you have to click generate level (no need to change 15) , then when you load my level, it will be reset to 30, the length of that level...and you can go up and down in it...change stuff , save it....

I've found the code on the net, took out some parts I did not need....

Unfortunately the following did not work with dynamic arrays :



procedure TForm2.Button2Click(Sender: TObject);
var f:file;
begin
if OpenDialog1.Execute then
begin
assignFile(f,OpenDialog1.FileName);
reset(f,1);
blockread(f,map,sizeof(map)); // load map
closeFile(f);
// assign name again
Edit1.Text:=map.name;
end;
end;


Check out my editor, and leave a comment. The tiles are inspired (and NOT COPIED) from warcraft 2 tileset, I did them myself...

Greetings
Robert

SilverWarior
31-12-2012, 07:06 PM
Hi,

thank you for the great tips, unfortunately this did not work



BackGround[i, j].ImageIndex = Images.Item[map.data[i,j+Offset]];




Images.Items[map.data[i,j+Offset]]
is returning a TAspyhreImage result and not Image index.
Actually you are providing Image index to this line by yourself using:

map.data[i,j+Offset]


So final code should probably look like this:

BackGround[i, j].Image := Images.Items[map.data[i,j+Offset]];
asuming that BackGround[i,j].Image is of TAsphyreImage type.




Also I was wondering if I could solve my sprite animation possible problems later like this ?



procedure TMain.LoadLevel;
var i,j : integer;
begin
for j := 0 to 14 do
begin
for i := 0 to 19 do
begin
BackGround[i,j] := TBackGround.Create(SpriteEngine);
BackGround[i,j].ImageName:= 'Empty.image';
BackGround[i,j].X:=i*32;
BackGround[i,j].Y:=j*32;
BackGround[i,j].Z:=1;
if BackGround[i,j].PatternCount > 1 then
begin
BackGround[i,j].AnimPos:=random(3);
BackGround[i,j].AnimCount:=3;
BackGround[i,j].AnimSpeed:=0.010;
BackGround[i,j].AnimLooped:=true;
BackGround[i,j].DoAnimate:=true;
end;
end;
end;



I belive it should work if PatternCount is automaticly calculated. Try setting a breakpoint inside a if clause to see whether code inside it do executes when needed. If not then probably PatternCount is always returning 1.

In your program you are using next line:

BackGround[i,j].AnimPos:=random(3);

This can break animation for continuos tiles like water since neighboring tiles are not synchronized (have different animation position). SO I strongly suggest that you get rid of it. Also you should note that generating random numbers is quite slow so if you call it often it can affect your program performance quite a bit.
Now I do understand that you would probbably like to have some random animations in case of some animated buildings so that they don't look just like clones of each other. But ass soon as you are dealing with some continuos tiles or contected tiles you need to synchronize their animation ogether.


I've found the code on the net, took out some parts I did not need....

Unfortunately the following did not work with dynamic arrays :


procedure TForm2.Button2Click(Sender: TObject);
var f:file;
begin
if OpenDialog1.Execute then
begin
assignFile(f,OpenDialog1.FileName);
reset(f,1);
blockread(f,map,sizeof(map)); // load map
closeFile(f);
// assign name again
Edit1.Text:=map.name;
end;
end;


The reason why this probably doesn't work is becouse you would need to first set the size of your arrays manualy by using:

SetLength(Map,Width,Height);

But I would rather suggest that you make your own map file type which would include all the needed information (width, height, number of layers, map name, etc); Making this would require a bit more code but if done properly would prevent you from having trouble when trying to load unsuported file.
If you want I can lend you some help in this but I need to know what kinda data structure are you trying to use in your map.

Also nice work on water texture.

robert83
01-01-2013, 11:00 PM
Happy new year!!!

Thank you for the tips, I did remove the random... also in my previous version (probably you did not check my source), I've already managed to save my map data using 2 for cycles, and load it as well.

Since then I've gone a bit further, I've modified my map record like this :


TMapData=array of array of Integer;
TMapAnim=array of array of Integer;
TMap=record
name:string[20];
map_length:string[3];
data:TMapData;
anim:TMapAnim;
end;


It stores the tile index, and it also stores for each tile index if it should be animated or not.

I'm also saving the data correctly map.name, map.length, map.data, map.anim , and loading correctly as well. But now I have a tiny problem , I don't know how can I make my water tiles get synced, right now if I move fast up or down on this
map I drew, they get out of sync.

(yeah I've created the tiles using some tutorials...to see how my map would look like).

I'm planing to also save into my map file, anim_start, anim_speed so for example if I want to add some light (for example airport lights) to my map, I could specify in which order they would light up, so they would not look like clones of each other.

I don't know maybe if I add anim_start and anim_speed into my map file , my water tiles will be synced again?

As for the whole thing I don't know myself what I wanna do... My plan is to make a Space Shooter game...that as you can see will have a lot of terrain in it...with moving stuff. Right now I wanna properly finish layer1 , then I'll add another layer
for trees, houses, other stuff, some floating on water stuff...., and much much later, I'll probably add another one Layer3 enemy objects, kinda like spawning points for enemy units, enemy buildings that shoot at you etc... but as I said I'm focusing on layer1, I don't want to overcomplicate and get lost, still got very-very-very much to learn about how exactly this sprite_engine works, and Asphyre as well, and game programing in general.

Attached I give you my latest Editor 0.7 with my tileset, I know it's bad , I will later revise it a few times for the final product... just wanted to see how the final picture would look like.
I've attached it again, the previous attachment had a nasty bug...

Greetings
Robert

robert83
02-01-2013, 12:27 AM
Hi,

Meanwhile I've managed to simplify down my DoAnimate yes or no logic.... here is the new code :



procedure TMain.LoadLevel;
var i,j : integer;
begin
for j := 0 to 14 do
begin
for i := 0 to 19 do
begin
BackGround[i,j] := TBackGround.Create(SpriteEngine);
BackGround[i,j].ImageName:= 'Empty.image';
BackGround[i,j].X:=i*32;
BackGround[i,j].Y:=j*32;
BackGround[i,j].Z:=1;
BackGround[i,j].AnimStart:=1;
BackGround[i,j].AnimCount:=3;
BackGround[i,j].AnimSpeed:=0.01;
BackGround[i,j].AnimLooped:=true;
end;
end;
end;
procedure TMain.DrawLevel;
var i, j: Integer;
begin
for j := 0 to 14 do
begin
for i := 0 to 19 do
begin
Used_Tile := Images.Item[map.data[i,j+Offset]];
BackGround[i,j].ImageName:= Used_Tile.Name;
if BackGround[i,j].PatternCount = 1 then
begin
BackGround[i,j].DoAnimate:=false;
end
else
begin
BackGround[i,j].DoAnimate:=true;
end;
BackGround[i, j].Draw;
end;
end;
end;



The problem still persists.... my water tile animation are no longer synced....how can I sync them? It does not matter if all animation would be synced, since I will use the map.anim to specify from which frame to start the animation , and add the map.animspeed to the mix, to make things like lights etc... be "unique" .

Greetings
Robert

robert83
02-01-2013, 11:52 AM
Ok,

I've been thinking about it... and I came to a solution... I created a new procedure SyncAnim, which I right now manualy set using a button on my form... after DrawLevel, I execute it and all my tiles are synced. (later I plan to make the AnimStart variable be read from the map.anim_start[] 2d array.



procedure TMain.SyncAnim;
var i, j: Integer;
begin
for j:= 0 to 14 do
begin
for i:=0 to 19 do
begin
BackGround[i,j].AnimPos:=0;
end;
end;
end;


Right now I'm thinking about where to put this code , so that It would execute automagically , each time I go up and down on the level, or add a new tile....

I'm thinking about using another boolean variable


var something_happened : boolean;

and if I go up or down, or add a new tile , something_happened :=true ...

then on AsphyreDevice1Render after DrawLevel I add


if something_happened = true then
begin
SyncAnim;
Something_happened:=false;
end;



Right now i don't know if there is a better way to do this? I think this should not slow things down, since I'm not re-drawing twice, I just sync the animpos for all my tiles to 0 right now...

UPDATE :

changed my code to see how this works , but it seems this is not the right solution...during scrolling the animpos is always reset to 0, the game will move trough the world by 1 instead of 32 , which will make my water animation along with the rest of the animation still...

UPDATE2 :



if Update_Anim = true then
begin
DrawLevel;
SyncAnim;
Update_Anim:=false;
end;


Attached is my code with the executable, and a sample level02.map , next step if this is OK, to create a level test sorta thing... that scrolls trough the level smoothly.

I think I've got it this seems to work quiet good

Greetings
Robert

SilverWarior
02-01-2013, 02:29 PM
The main reason why your sprites are not synchronized is the fact that when you are creating them or modifying them upon map move you are setting their current animation position to 0 but instead you should set animation position to the current animation position.
So basically you need to keep track of water animation. You can do this by having one Global numerical variable which is increased at the same intervals as animation pictures needs to be changed. And then to find out which is current animation position you calculate using Mod command:

AnimationPos := GlobalAnimCounter mod AnimationCount;
If you do this you shouldn't forget to reset GlobalAnimCount to 0 after some time othervise you will exceed maximum integer value which can lead to problems.

Anywhay I'm currently at work so Iall I can give you now is general idea. But when I come home I intend to download Aspyhre eXtreme and SpriteEngine so I would be able to actually compile your source and therefore be able to test some ideas directly by changing your code.
I have already given some thoughts about how to make map smothly scrolable to be used in your game but I need to check whether it is posible to do this using your sprite engine.
I was also thinking about how to implement your game map in most eficient way (what would be the best way of storing information for all your needed layers).

So stay tuned and await more of my suggestions shortly.

robert83
03-01-2013, 01:00 AM
Hi,

I can't sleep, I've coded a bit, added AnimStart variable to my map data... you can set animstart for each tile, so the latest attached editor can make certain tiles be synced, and other tiles see them lights do their animation from different starting position.
Also since now I'm only executing my DrawLevel and SyncAnim loop when the map is updated or moved.

If you execute this one, and load the included level02.map you'll see that water is perfectly synced, the lights which i wanted to be synced are synced, and those 3 lights are using different anim start position, so don't look like exact copies of
each other.

Next I'm planing to add for each animated sprite , animation speed, so I can have faster and slower lights, or faster slower water,fire...etc....


I was thinking myself about the scrooling, unfortunately I cannot simply use BackGround[i,j].WorldX and WorldY , because I'm only drawing as many sprite's as I'm actually using for 1 screen...

I came up with a solution, which I think could work, I would create 1 more line of Sprite's , that would be beyond the screen height in the direction I'm moving.

Then I would move WorldY until I would rich the end of this aditional (hidden) line of Sprite's , once I rich the end of it I would move my entire BackGround[i,j].Images in the apropriate directions, and reset my WorldY to the original position.

My idea is that the redraw of the Sprite Grid would be so fast that the user would not notice it, and would "feel" like it's scrolling trough the map by 1 instead of 32 .

Greetings
Robert

robert83
03-01-2013, 01:55 AM
Well, I almost got it :) I made the smooth scrolling possible like this :



procedure TTools.DOWN_1Execute(Sender: TObject);
var i,j : integer;
begin
if MainForm.Offset < strtoint(LLength.Text)-14 then
begin
inc(MainForm.Reset_Grid,1);
if MainForm.Reset_Grid = 32 then
begin
Main.DrawLevel;
Main.SyncAnim;
MainForm.Reset_Grid:=1;
inc(MainForm.Offset,1);
for j := 0 to 15 do
begin
for i := 0 to 19 do
begin
BackGround[i,j].WorldY:=0;
end;
end;
end;
for j := 0 to 15 do
begin
for i := 0 to 19 do
begin
BackGround[i,j].WorldY:=BackGround[i,j].WorldY-1;
end;
end;
end;
end;



Currently it only works for down, but has a small problem which I'm trying to fix now, when I start scrolling down with it goes down 32 , then does a jump that I can feel.... but after that I cannot feel that I'm drawing that one additional line , it feels like
I'm actually scrolling trough a 30 line long map for example....
Also my animation is reset as I move down .... which is not good...damn, have to find a fix for that to.

But atleast did some progress on me own.

UPDATE : if I set RESET_GRID on FormCreate to RESET_GRID:=31 (instead of 0), my scrolling down is as smooth as can be.... only animation remains problem now that it's synced whenever I move....not good will have to
use you're suggestion to make em synced , hopefully I can make em synced and still keep the uniqueness of invidiual animations by using startpos by tile.

Greetings
Robert

robert83
03-01-2013, 02:18 AM
YEAAAHHH,

Here it is, smooth scroll works in both directions up and down, only minor problem which I need to fix now is (not a big problem for my intended game since it's a space shooter which goes in one direction only) , when I change directions it's visible. I find it difficult to explain with my english knowledge. See for youreself.

Anyway making some really nice progress :) (so I think atleast)

Greetings
Robert

SilverWarior
03-01-2013, 02:36 AM
Hi,

I can't sleep, I've coded a bit, added AnimStart variable to my map data... you can set animstart for each tile, so the latest attached editor can make certain tiles be synced, and other tiles see them lights do their animation from different starting position.
Also since now I'm only executing my DrawLevel and SyncAnim loop when the map is updated or moved.

If you execute this one, and load the included level02.map you'll see that water is perfectly synced, the lights which i wanted to be synced are synced, and those 3 lights are using different anim start position, so don't look like exact copies of
each other.

Nice work on this. But I still think that water animation gets out of sync when scrolling.


Next I'm planing to add for each animated sprite , animation speed, so I can have faster and slower lights, or faster slower water,fire...etc....

Yes this would be nice feature and could come verry handy in future.




I was thinking myself about the scrooling, unfortunately I cannot simply use BackGround[i,j].WorldX and WorldY , because I'm only drawing as many sprite's as I'm actually using for 1 screen...

I came up with a solution, which I think could work, I would create 1 more line of Sprite's , that would be beyond the screen height in the direction I'm moving.

Then I would move WorldY until I would rich the end of this aditional (hidden) line of Sprite's , once I rich the end of it I would move my entire BackGround[i,j].Images in the apropriate directions, and reset my WorldY to the original position.


Yes adding a new line of sprites for smoth scrolling is necessary. But I had a bit different idea of how to implement this. Instead of moving WorldY Position I would go and move the sprites instead. Why? If you move sprites instead you won't need to update all visible sprites when you have moved your map for the full line size but instead you go and destroy those sprites that are no longer needed and then make new line of sprites when they would be needed.

Since you are trying to make SpaceShooter game I presume you will be moving your gameworld downward. So the way how I would implement this scroling is like this.
1. First you create enough sprites to fill window size (similar as you do now).
2. Then you create another line of sprites above current ones.
3. You move all these sprites downward by certain amount of pixels.
4. Once you have repeted step 3 enough times so that you moved the world for entire line height you destroy botom line of sprites which aren't visible anymore now and then you create another line of spites at the top. After that you go back to step 3.
5. You repeat step 3 and 4 until you either reach the top of the map or reach a boss ship which temporarily halts your progression through map.
6. Then you either end the game if it is on end of the map or wait for the boss to be kiled.

The biggest advantage of this approach is that you are not updating the textures for existing Sprites (unles for animations) but you are just adding necessary new Sprites and killing (destroying) unnecessary ones.

Offcourse to make this work easier I would change the map structure to someting like this:

TTileData = record
ImageIndex: Integer; //Stores the index for retriving necessary image from TAspyhreImages
AnimationCount: Integer //Stores how many pictures does animation contains
AnimationOffset: Integer; //Stores offset from global animation position
end;

TMapLine = Array[0..15] of TTileData; //Stores the whole line of cells asuming the map width is 15 tiles.

TMap = record
Mapname: String;
MapSize: Integer //Storing the map size in String type could lead to problems during conversion. Also storing in Integer type saves you some memory.
TLines: Array of TMapLine; //Stores the whole map
end;

So with proper file handling routines you can now actually go and read the map data directly from file Line by Line and thus reduce Games memory consumption a lot and you gain the ability to have enourmus maps if needed.
Ofcourse implementing several Layers would reqjuire aditional change to this maps structure.

As for Sprites animations you can see that I used AnimationOffset variable in my suggested map code. The reason for this is the fact that I'm still thinking of having some Global variable to progress position of various animations so that when you are making new sprites you just set their proper aniamtion position and then leave your SpriteEngine to take care of your animations.
To put this in code I need to get my computer set to be ready to compile your program first. Yes I know I sad that I would do this as soon as I come home but the fact is that I came home preaty beat up today so I haven't got much motivation for programing. Tis also means that above code is still just out of my head and might have some syntax errors.

SilverWarior
03-01-2013, 02:45 AM
YEAAAHHH,

Here it is, smooth scroll works in both directions up and down, only minor problem which I need to fix now is (not a big problem for my intended game since it's a space shooter which goes in one direction only) , when I change directions it's visible. I find it difficult to explain with my english knowledge. See for youreself.

Anyway making some really nice progress :) (so I think atleast)

Greetings
Robert

From what I see the problem is that when you are moving UP your are not adding necessary line of sprites on uper side which makes that black line.

Any whay now if you would excuse me I would like to go to bed as it is 3:41 am here now.
Post any progress you made and I'll try to find some time tomorow to write you the whole map movment functions as explained in my previous post. I would also write you a Map file handling routines which would alow you to load several Layers when needed.

EDIT: BTW I recomed you to start commenting your code. While at this time it might seem unnecessary to do this but when your code grows you will find out that you can quickly get lost in it and then don't know what which part of the code does.

robert83
03-01-2013, 09:58 AM
Hello,

thank you , I will start commenting, and cleaning up the code, possibly using more units, try and categorize the procedures in units in a meaningfull easy to see manner.
yeah now I see what you mean, though I'm way to tired to fully understand (went to sleep at 3:00 am, woke up at 06:30 am, at work now... will sleep today).
if I understand you're map format would allow map file to be read line by line when needed ? instead of beaing read at once?
once I get some sleep, I will modify the code, and I'll try to do it like you've told me .

Now that I think about it, I will not overcomplicate my life. I'll reamove up and down by 1 , instead I will put a simple demo mode there, which when clicked on will jump to the end of the map, and from there move up by one till the end of the map. Just like it would later in the real game. And I think I will also add another variable into the map data.... scroll speed, migh come in handy if later I decide I want a map that is faster for some reason then the rest.... ligthspeed travel or escape from some boss, or something.

Thank you for youre time, I'll post tomorrow on my progress.

Greetings
Robert

User137
03-01-2013, 10:11 AM
Is this about drawing visible map section of 2D grid? You don't need to add any variables to that for background class. It's just small tricks you do in the Draw (DrawLevel?) function.

1) You have worldX, worldY: single, floating point coords yeah? To get deltaX, deltaY, you can use function frac() for frac(worldX), drac(worldY). I don't know about your scaling, but on all maps i have done, 1.0 value in coords matches size of 1 tile. So if i draw player on 0.5, 0.5, it's in the middle of first tile.

2) Getting drawable area should be done from the rendering context, be it TForm, TPanel or TOpenGLContext. SX:=context.Width div TileWidth, SY:=context.Height div TileHeight.

3) When it comes to drawing...


iX:=floor(worldX);
iY:=floor(worldY);
for j:=iY-1 to iY+SY do
if (j>=0) and (j<SizeY) then
for i:=iX-1 to iX+SX do
if (i>=0) and (i<SizeX) then begin
DrawTile(i-iX+deltaX, j-iY+deltaY, @tile[i, j]);
end;
That's just from memory though, should work without much modifications.

SilverWarior
03-01-2013, 09:54 PM
You don't need to use more units. the only reason for using more uits is when you are trying to make part of the code so that it can be reusable in some other project of yours (you just add existing unit to another project).
Also you don't need to make procedure or function for about everyting. You only use procedures or functions for comonly repeated parts of code.

What I do suggest is simply use basic comenting of your code (you write simple coment on top of your procedure or series of source lines so nex time when you will look at that coude you will know what it is for.
I'm not usre which development IDE are you using but if your development IDE supports using of Regions (available in Delphi XE or newer and in FPC) learn how to use them.
The best feature of Regions is that they alow you to make foldable parts of your code and even name them. While regions don't affect the program execution they do provide a way to make your code easily redable.

About scrolling capabilities while removing smoth capability to scroll your map up and down isn't needed for your editor do make sure that you leave yourself the ability to scroll by line othervise you will just make life for yourself a bit more compicated

SilverWarior
04-01-2013, 12:48 AM
Damn I can't seem to get old Asphyre 3.1.0 eXtreme to sucsessfully compile on my Delphi XE3.
Also the sprite engine itself is being dependant on some Units that are not present in newest Asphyre Sphinx so I can't get your sprite engine to work with newest Asphre engine.

Which development IDE are you using anywhay?
EDIT: Don't mind my last question I already got an answer using PE Explorer :D

I gues I need to get my old laptop out of all that dust which acumulated over past years on it, fire up Delphi 7 and try to make precompiled packages for both Asphyre 3.1.0 eXtreme and SpriteEngine so I might be able to use them with Delphi XE3

robert83
04-01-2013, 02:51 PM
Hello,

thank you, I've modified myself the Asphyre eXtreme source I use, since dxbase.pas conflicted with devExpress components I use, attached I give you the modified Asphyre eXtreme sources directory zipped , only changed unit names where needed in order to work along with devExpress.

Maybe I wasn't clear, I do intend to leave alone line by line scrolling for the editor.

I only intend to remove smooth scrolling in both directions, that is not needed only complicates my life, I will be only scrolling one way trough the game world anyway. Will only keep smooth scrolling up. That is what I meant. The finel editor
should allow me to test the level , so it would look like the real game, enemy ships comming down and shooting at the player , only the player would be not affacted by this . That reminds me, I will also need to specify for example if Layer3 will only include enemy ships and maybe power up's , I will need to specify in which direction and with which speed will they go, how many lives will they have etc...Will need to think this trough , but I think it would be logical and simple if the map data would hold enemy ship type , life, direction data no? (but this is a bit down on the road...)

I'm using Delphi 7 SE , works fine on Windows 7 here.

About the Units I've said, I wanted to seperate em like this, anything related to level loading, drawing etc... will be in unit level for example.
Everything related to enemies will be in a unit enemies.

Right now I have some programing to finish here for the company I work for, so I'll be back on programing this editor tomorrow hopefully.

EDIT :

meanwhile I've managed to test my editor, on a Duron 1400, SIS661FX_760_741, 512MB ram, Windows XP, got 30 FPS :) , only thing is in non full screen mode, the screen flickers ... on the Draw surface, but it dissapears in Full Screen mode, this is good. This is quiet a low spec system, Windows XP barelly works on it, not to mention the "GPU".

Greetings
Robert

robert83
10-01-2013, 10:58 AM
Hello,

SilverWarriror, I've changed my map layout to the one youv'e suggested :


TTileData=record
ImageIndex: integer; // Tile Number
AnimStart: integer; // Tile Animation Start Position
AnimSpeed: Single; // Tile Animation Speed
AnimCount: Integer; // Tile Animation Count
end;
TMapLine=array [0..19] of TTileData; // Stores one line of tile data
TMap=record
name:string[20]; // Name of the Map
map_length:integer; // Length of the Map
Lines: array of TMapLine; // stores the Entire map [0,0..19] , first value is 0 till map_length (vertical), second value is 0..19 (horizontal)
end;


I've also managed to get smooth scrolling working in one direction using your method (took me a while I know, had some brainfog issues, was sitting before this damn computer for many hours and nothing, couldn't even start...then yesterday it suddenly hit me.

This is the code that moves the map down ( I'll reverse my logic...and guess it'll work for up )



procedure TMain.TestExecute(Sender: TObject);
var x,y : integer;
new_line : boolean;
begin
if Offset < Map.map_length-14 then // -14 , Number of Tile Lines Drawn on Screen
begin
inc(Counter,1); // I'm using this to check if I've scrolled down one tile Height (32), if I did
if Counter = 32 then // then I'm increasing Offset by 1 and reseting Counter to 0, Offset is used to tell the new line of sprites which Line of TileData
begin // to read from the array
Inc(offset,1);
Counter:=0;
end;
for y:=0 to 15 do
begin
for x :=0 to 19 do
begin
BackGround[x,y].Y:=BackGround[x,y].Y-1;
if BackGround[x,y].Y = -32 then
begin
BackGround[x,y].Dead;
new_line := true;
end;
if new_line = true then
begin
BackGround[x,y]:= TBackGround.Create(SpriteEngine);
Edit9.Text:=inttostr(offset);
BackGround[x,y].ImageName:= Images.Item[map.Lines[offset+15,x].ImageIndex].Name;
if BackGround[x,y].PatternCount = 1 then
begin
BackGround[x,y].DoAnimate:=false;
end
else
begin
BackGround[x,y].AnimStart:=0;
BackGround[x,y].AnimCount:=map.Lines[offset+15,x].AnimCount;
BackGround[x,y].AnimSpeed:=map.Lines[offset+15,x].AnimSpeed;
BackGround[x,y].AnimPos:=(GlobalAnimCounter mod BackGround[x,y].AnimCount)+map.Lines[offset+15,x].AnimStart;
BackGround[x,y].DoAnimate:=true;
BackGround[x,y].AnimLooped:=true;
end;
BackGround[x,y].X:=x*32;
BackGround[x,y].Y:=480;
BackGround[x,y].Z:=1;
new_line:=false;
end;
end;
end;
end;
end;



Everything is nice with this, works like magic, but I have one problem... the animation is not in sync, I'm using GlobalAnimationCount as you've suggested (it works when I scroll trough the map line by line, but not when I do this smooth scrolling)

EDIT : actually the newly created TILE Lines are in sync , meaning each New line which has 15 Sprites , all of them 15 sprites are in sync, but everytime a new line is created it's not in sync with the previous line.

This is how GlobalAnimationCount is increased


if GlobalAnimCounter = 1000 then GlobalAnimCounter:=0;
inc(GlobalAnimCounter,1);


This above is defined on AsphyreDevice1.Render.


Any idea why my anim is out of sync when "smooth" scrolling down, but not out of sync when scrolling line by line. I'm doing the same thing on Drawlevel and there it works :



procedure TMain.CreateSprites;
var x,y : integer;
begin
// this procedure creates the starting sprites for one screen and fills them with Empty.image
for y := 0 to 15 do // vertically
begin
for x := 0 to 19 do // horizontally
begin
BackGround[x,y] := TBackGround.Create(SpriteEngine);
BackGround[x,y].ImageName:= 'Empty.image';
BackGround[x,y].X:=x*32;
BackGround[x,y].Y:=y*32;
BackGround[x,y].Z:=1;
end;
end;
end;

procedure TMain.DrawSprites;
var x,y: Integer;
begin
// this procedure changes the images for every sprite created at startup, creating the illusion of scrolling trough the map
// line-by-line
for y := 0 to 15 do // vertically
begin
for x := 0 to 19 do // horizontally
begin

BackGround[x,y].ImageName:= Images.Item[map.Lines[y+Offset,x].ImageIndex].Name;
if BackGround[x,y].PatternCount = 1 then
begin
BackGround[x,y].DoAnimate:=false;
end
else
begin
BackGround[x,y].AnimStart:=0;
BackGround[x,y].AnimCount:=map.Lines[y+Offset,x].AnimCount;
BackGround[x,y].AnimSpeed:=map.Lines[y+Offset,x].AnimSpeed;
BackGround[x,y].AnimPos:=(GlobalAnimCounter mod BackGround[x,y].AnimCount)+map.Lines[y+Offset,x].AnimStart;
BackGround[x,y].DoAnimate:=true;
BackGround[x,y].AnimLooped:=true;
end;
end;
end;
end;


EDIT : I was thinking that maybe I should use another counter , which would count the time between two created sprites ?

1 I create the initial sprites 16 lines 0..15 .
2 I start moving the sprites up (lines 0..15) and start the differential_timer
3 I destroy spriteline which is -32 , and add a new sprite line and reset differential timer to 0 ( here I'm not sure... I maybe do this ?


BackGround[x,y].AnimPos:=(GlobalAnimCounter-differential_timer mod BackGround[x,y].AnimCount)+map.Lines[offset+15,x].AnimStart;

)

and I keep repeating step 1,2,3 till I reach the end. This idea any good?
Greetings
Robert

SilverWarior
10-01-2013, 03:22 PM
From your code I see that you are resetting your Global Animation Counter when it reches 1000. If I remember corectly your water animation has 3 pictures. So as 1000 is not dividable by 3 this creates a problem. So you should reset your counter at 999 and you should go and reset it to1 or if you have first animation picture at index 0 you should reset your counter at 998 and then reset it to 0.
But there will be even bigger problem once you introduce animation with varios speeds and various lenghts. Why? Becouse you will need to find a right point when to reset your counter (first point when all animations would reach the starting position).
Best way to do this is to calculate how much time each animation loop lasts (animation lenght * animation speed - where animation speed determines time between animation updates) and then find a time when all these animations will reach to starting position at the same time.
You do this by finding first number which is dividable with all theese loop times. If my memory servews me correct there is a function in Math unit to do this. Do note that this function can be quite slow at some times.
Workaround for this is to simply multiply all these loop times between each other as this will give you certain point in time when all these animations do get to starting position but ti might not be the first time when this happens.
So for instance if you have three different animations first lasts two seconds, second lasts three seconds and third lasts 5 seconds you will the time when all of theese gets to starting position at the same time using 2*3*5=30. So int this case you could reset your Global Animation Counter every 30 seconds.

I hope my explanation is understandable enough. I'm also sorry that I haven't yet prepared my programing enviroment to be able to compile your project. After about thee years I had some problems with my laptop for the first time which required some troublehunting. In the end it turned out that some files belonging to Eset antivirus software were damaged which in some ocasions caused unexpected program behavior. Also I noticed some system slowdows incase of large HDD activity but I'm not yet sure whether this is due to some software problem or it is hardware related (HDD S.M.A.R.T reports shows no anomalies or problems, but there is ocasional clickig sound coming from HDD but no Bad sectors yet).
Best solution would be for me to buy a new disk drive probbably SSD and instal it as second drive (my laptop actually supports usage of two SATA 3 hard drives) and reinstal my whole operating system. This would alow me to keep all the old data so that incase I screw up something I don't use any data (I do have lots of important data on my Laptop and no space to make a safety backup of it). And another problem is that as I didn't had Windows 7 before my data isn't quite organized.

robert83
10-01-2013, 03:47 PM
Hi,

OFFTOPIC

or use Virtual Machine with Windows 7 , I actually use windows xp virtual machine to administer my samba domain controllers...
or just install Delphi 7 on Win7 , or Delphi 7SE if you wish...it works.

OFFTOPIC

Anyway I've decieded to attach my latest source, I've redid the entire window, this should be fully visible on your laptop screen (it is on mine... think it's 1280x800 or something....)

You open up the editor...then click on FILE-OPEN level02.map ...then press the up arrow key (don't ask...why I used it for down :) :) )

I've completely removed the GlobalAnimCounter reset...as you can see it goes till it reaches it's limit and probably makes the app crash or something....

As you can see when you press UP arrow , and the map moves down... each new sprite line is created (every sprite on the new line is in sync with each other) , but each NEW sprite line is a bit off from the previous creating my wave effect....

Greetings
Robert

User137
11-01-2013, 08:58 AM
If you must use such global animation counter, couldn't you make it single type, and run from [0.0 .. 1.0[ (means 1.0 isn't included)? Then just scale each animation to its specific framecount.

AniPos:=trunc(GlobalCounter*FrameCount);


GlobalCounter:=GlobalCounter+0.02;
if GlobalCounter>=1 then GlobalCounter:=GlobalCounter-1;

phibermon
11-01-2013, 01:12 PM
Ok so your basic issue is synchronizing the animation of sprites, that may of been added at any time during the animation of the others.

There are many ways you could do this (although unless you have two co-dependant animations I can't see why you'd want this) but rather than incrementing every single sprite for the next frame of animation, how about you group the same sprites together or sprites that have the same number of frames? IE:

SpriteGroup1_FrameNumber = 3

for I := 0 to SpriteGroup1_SpriteCount-1 do
Spites[I].CurrentFrame = spritegroup1_framenumber;


That way you can ensure that for animations of the same speed/number of frames, that they are always rendered on the same frame, regardless if one was added halfway thru the animation cycle.

Now as far as synchronzing animations of different lengths, why? scheduling so they start at the same time will mean that at some point, you'll have something that should be on screen, but isn't because it's waiting for the start of the next cycle.

if you have animations of different lengths that you wish to synchronize, then you should scale the frame-rate of one of the animations so they both take the same amount of time to run.

--

I'm currently extending my 3D animation lib with better interpolation between animations. Here's an example to get you thinking :

Walk animation -15 frames - 0.8 second cycle from left foot to left foot. actual movement Speed = 3
Run animation - 20 frames - 0.4 second cycle """ actual movement Speed = 5

So how do you smoothly interpolate the skeleton between frames so you can blend walk into run and visa versa?

The trick is (other than other artist/bone related stuff to make animations that will blend well) that you scale both
animations at the same time. so if walk has an anim cycle of 0.8, and run 0.4, then halfway through the blend, both animations are scaled so they both have a cycle of 0.6 and a speed of 4 (exactly half way between the two).

So it doesn't matter where in the animation you start running or walking, it'll always be able to interpolate fairly well.

--

As far as sprites are concerned, what's so offputting about the non synchronized animations? if you created a forest you want all the trees to sway in sync?

Like I say, unless your animations are co-dependant and require sync (two sprites throwing a ball at each other) then you should probably not bother.

Oh and if you absolutely must sync different length anims so they start on the same frame, then just add dummy frames to the shorter one so it's the same number of frames. yes that'll use slightly more more memory but you'll save yourself a whole bunch of code. Combine that with grouping of sprites

robert83
11-01-2013, 06:34 PM
I must have perfectly synced sprites somehow, I don't want to complicate my life with Sprite Groups...
For example I might want to have some object that consists of 4 tiles do something out of sync, other times in a certain order, or in perfect sync. I believe it should be doable to sync newly created tiles with previously created tiles, only problem is this seems to be beyond my knowledge right now...

I've tried to do it like this :

1. Created a new 2d array , that is responsible for holding the current animation position for every tile on my tile map ( I though baaammmmmmm eat this EVIL sprite engine time offset demon )


TAnimTable=array of array [0..19] of single;

2. I've created the following procedure :


procedure TMain.AnimCounter;
var i,j: integer;
begin
for i:=0 to Map.map_length do
begin
for j:=0 to 19 do
begin
if map.Lines[i,j].AnimCount > 1 then
begin
AnimTable[i,j]:=AnimTable[i,j]+(1*map.Lines[i,j].AnimSpeed);
end
else
begin
AnimTable[i,j]:=1;
end;
if AnimTable[i,j] > map.Lines[i,j].AnimCount then AnimTable[i,j]:=0;
end;
end;
end;


This I've checked (using a few EDIT boxes ) works, for example Animated Sprite at 0,0 has 3 animation positions , and it's speed is 0,01 ... I've checked the position goes up with the apropriate speed.
I've added another Sprite at 0,1 which has 6 animation positions, and it's speed is 0,5 , I've checked it again it goes at a much faster rate...

I thought to myself YEAH I solved this darn problem... (and the program is still pretty fast, even on me amd duron 1400+ , with my test level...which is not to long , but I don't plan to make enormous levels...a dude has to start somewhere...)

I've modified my smooth scroll test procedure a bit :



procedure TMain.TestExecute(Sender: TObject);
var x,y : integer;
new_line : boolean;
begin
if Offset < Map.map_length-14 then // -14 , Number of Tile Lines Drawn on Screen
begin
inc(Counter,1); // I'm using this to check if I've scrolled down one tile Height (32), if I did
if Counter = 32 then // then I'm increasing Offset by 1 and reseting Counter to 0, Offset is used to tell the new line of sprites which Line of TileData
begin // to read from the array
Inc(offset,1);
Counter:=0;
end;

for y:=0 to 15 do
begin
for x :=0 to 19 do
begin
BackGround[x,y].Y:=BackGround[x,y].Y-1;
if BackGround[x,y].Y = -32 then
begin
BackGround[x,y].Dead;
new_line := true;
end;
if new_line = true then
begin
BackGround[x,y]:= TBackGround.Create(SpriteEngine);
Edit9.Text:=inttostr(offset);
BackGround[x,y].ImageName:= Images.Item[map.Lines[offset+15,x].ImageIndex].Name;

if BackGround[x,y].PatternCount = 1 then
begin
BackGround[x,y].DoAnimate:=false;
end
else
begin
BackGround[x,y].DoAnimate:=true;
BackGround[x,y].AnimLooped:=true;
end;
BackGround[x,y].X:=x*32;
BackGround[x,y].Y:=480;
BackGround[x,y].Z:=1;
new_line:=false;
end;
BackGround[x,y].AnimStart:=round(AnimTable[x,y+Offset]);
BackGround[x,y].AnimCount:=map.Lines[y+Offset,x].AnimCount;
BackGround[x,y].AnimSpeed:=map.Lines[y+Offset,x].AnimSpeed;
end;
end;
end;
end;


The AnimCounter procedure is executed at AsphyreDevice1Render .

And I still get the cursed wave effect , how....is....this....possible ????

Now that I think of it...maybe the problem is...that now since I'm doing the position calculation... maybe I should manually step the images? Can I do that somehow?
The idea here was to calculate all animation positions for every tile , even uncreated... thus eliminating the lag caused by it's creation... I think it should work...if I could somehow manually step the Sprite Position
every time when the TEST action runs to my AnimTable[x,y] position... I can even add Animation Offset into this combo...


Greetings
Robert

robert83
11-01-2013, 09:48 PM
I DID IT!!!!!!!!!!!!!!!!!! AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA


procedure TMain.TestExecute(Sender: TObject);
var x,y : integer;
new_line : boolean;
begin
if Offset < Map.map_length-14 then // -14 , Number of Tile Lines Drawn on Screen
begin
inc(Counter,1); // I'm using this to check if I've scrolled down one tile Height (32), if I did
if Counter = 32 then // then I'm increasing Offset by 1 and reseting Counter to 0, Offset is used to tell the new line of sprites which Line of TileData
begin // to read from the array
Inc(offset,1);
Counter:=0;
end;

for y:=0 to 15 do
begin
for x :=0 to 19 do
begin
BackGround[x,y].Y:=BackGround[x,y].Y-1;
BackGround[x,y].AnimPos:=AnimTable[y+Offset,x];
Edit2.Text:=formatfloat('####.##',AnimTable[y+Offset,x]);
if BackGround[x,y].Y = -32 then
begin
BackGround[x,y].Dead;
new_line := true;
end;
if new_line = true then
begin
BackGround[x,y]:= TBackGround.Create(SpriteEngine);
Edit9.Text:=inttostr(offset);
BackGround[x,y].ImageName:= Images.Item[map.Lines[offset+15,x].ImageIndex].Name;
if BackGround[x,y].PatternCount = 1 then
begin
BackGround[x,y].DoAnimate:=false;
end
else
begin
BackGround[x,y].AnimStart:=0;
BackGround[x,y].AnimCount:=map.Lines[offset+15,x].AnimCount;
BackGround[x,y].AnimSpeed:=map.Lines[offset+15,x].AnimSpeed;
BackGround[x,y].AnimPos:=AnimTable[y+Offset,x];
BackGround[x,y].DoAnimate:=true;
BackGround[x,y].AnimLooped:=true;
end;
BackGround[x,y].X:=x*32;
BackGround[x,y].Y:=480;
BackGround[x,y].Z:=1;
new_line:=false;
end;
end;
end;

end;
end;



Had it reversed on AnimTable[x,y]... [y+Offset,x] is correct... damn I'm getting lost on my arrays... anyway I'll clean up the code a bit... and post the full source with the exe here... I've tried it again just to make sure...it works...no matter what I do
I scroll down a bit ...wait....scroll again....or scroll completely to the end... the result...perfectly synced sprites the way I like em...

EDIT : something is still off... I'll need to check the code ...I'm getting lost in my arrays.... I'm not reading the animpos correctly from the array for the vertical line... will recheck

Greetings
Robert

robert83
17-01-2013, 12:27 PM
Hi,

finally did it, it works now. Animation is synced, I'm using another array to keep track of the animations, I've modified the program a bit
if you decide to run it, you select FILE , OPEN MAP, select demolevel2.map from the directory of the program, then you click on TEST button
and it will start scrolling trough the map, when it reaches the end of it, it starts again.

Next I'm planing to add layer2 , any tips for me how to do it properly ? (SilverWarrior)

Also... I wonder , how I could do with this editor that so called "Paralax" scrolling . I was thinking that maybe I would create layer 1 that would be shorter, and layer 2 that would be longer . For example if Layer1 would be 30 lines long, Layer2 which would go at twice the speed would be 60. But now comes the problem, I would need to define map length for layer1 and layer 2 as well right ? How would you do it ?

I would like to know how I could create the following for example.

I saw the following in games which I liked . ( I'll try to describe how that seemed to me ) (JamesTown pc game)

Layer 1 was a very slow moving (far) background
Layer 2 was a normal moving background for example flying mountains, or other objects
Layer 3 was clouds which moved the fastest

Rright now I'm having problems even trying to figure out how to do it properly , how to start thinking about it. A few hints would be much appriciated.

Greetings
Robert

SilverWarior
17-01-2013, 08:42 PM
Found a bug :D
In your demo map at the ofset 75 those fast blinking Landing Lights are actually causing water animation to animate faster. I haven't yet looked through your code to figure out why this is happening. Will do as soon as I find some more time.
Another bug which probably isn't important for editor itself. During map testing the colision procedure doesn't take into account smoth sprite movment and if you do click to try placing tile at specific location during test phase it is placed in wrong line and whole map jumps either up or down can't tell exactly.

As for Paralax scrolling I personally have no expirience with this but I will take a look at this approach to learn more about it.

robert83
17-01-2013, 09:42 PM
Hello Thank you, but that is not a bug, I did that on purpose to see how it would look, I tried to create a river sorta thing with faster water...moving , and since the river goes into that smaller one the first two tiles are working at the same speed the rest are slower. That is a bug in my drawing , that is being lazy and not creating proper tiles... gona have to start it sooner or later. I think I will probably start working on my layer1 tiles , making em a lot more better...but it sure takes a hell of a lot of time to make em look good....I think I will do a different kind of water... don't like this... I was thinking about some cooler tilesest where the water is moving up and down on the shore... I think I don't need to make the water actually move everywhere...

As for the Editor thing, I did not intend to use it that way. I need to disable controls during the scrolling, but since it's intended for personal use to be used by only me...I'm not sure if it's worth it.

I was having a hell of a time with making the scrolling work, could not figure out why it would not work with my DrawSprites routine... so I needed to delete all the sprite lines before I recreated them again for scrolling... otherwise I was getting errors.
My original scrolling was from bottom to top and stop. And If I used DrawSprites procedure to draw the initial 15 bottom sprites and then create one in top of it... and then move it down, and add one at top till I reach the end. If I clicked on anything no matter
where on the drawing surface I got an error... I could not figure out why, then I started playing with the scolling trough the map and start again and then I noticed why, or I think why... the lines the way I recreated them were not in order...

Example I started like this : (for simplicity 4 lines only)
0
1
2
3

And depending on my map length but when I reached the end it was like this (probably)
2
3
0
1 -> this was destroyed because in our example the screen can use say 3 lines

Anyway this is something that I probably should not bother with, because I don't intend to use the editor and the scrolling at the same time...

So then let's postpone the paralax thing for now. If you can give me a few hints on how would you do the aditional layers ? Should I just simply add another TMapLine2 ... TMapLine3 ... TMapLine[n] ?

Also was thinking about the following (think it's doable...and I think it would look kinda cool...) I was thinking of adding speed to each line of the map. Meaning I could set the speed if I wanted to at line level, kinda like scripting the map a bit...
For example lines 1..12 would go at 8x... then til 75 I would go at 2x.... then at line 100 would go at 8x etc... So I could change the speed of the level depending on the situation, more enemies less enemies , escape etc...

Was thinking that I could implement it like this :

To the right side of my Panel1 (Asphyre surface) I would ad 15 edit boxes... , when i create the map I would specify the default speed and would fill up the array with it till end
Going down and up on the map I would fill the edit boxes with the apropriate speeds from the array ... and I could also change them.

Right now looks like this :


TTileData=record
ImageIndex: integer; // Tile Number
AnimStart: integer; // Tile Animation Start Position
AnimSpeed: Single; // Tile Animation Speed
AnimCount: Integer; // Tile Animation Count
end;


Would change it to this :



TTileData=record
Speed:integer // Scrolling Speed (2,4,6,8,16)
ImageIndex: integer; // Tile Number
AnimStart: integer; // Tile Animation Start Position
AnimSpeed: Single; // Tile Animation Speed
AnimCount: Integer; // Tile Animation Count
end;


I think I would use another 2 layers for background (that is just background nothing else) ... so I could place grass for example, and trees above grass...

The frouth layer would be by the current plan the layer that would tell the map for example that at 19,4 spawn enemey ship , type, life, speed, direction type, firing type etc... or spawn cloud , birds , etc... which means this last layer would have a lot of variables , I would like to do the magic at the map editor level . So I can create the "randomness" by hand... one ship here other ship there... or a stationary turret here there , but right now I only mentioned it so you can point me to the right direction with your helpfull tips / hints . Only thingking about it right now kinda overwhelms me. Gona do it by baby steps.

Anyway think I'll watch a movie now :) Nighty night!

SilverWarior
18-01-2013, 12:40 AM
GOOD news! I have finally managed to prepare development enviroment so I'm finally able to Compile and Debug your program. So expect more practical solutions (code samples) in future. :D
BAD news! After quick debug tracing through your code I see that there are several DANGEROUS sections (even if Asphyre engine doesn't manages to initialize you still try to initialize SpriteEngine which then fails as it needs already initialized Asphyre engine) which might lead to many bugs. ???

So what I'm gonna do first is study your code, try to find this sections and also try to fix them. Might take some time.
Then I'm gonna make some quick test to check SpriteEngine capability so I get some idea what we might be able to get out of it. This would probably largely affect my futurte suggestions.

As for the layers I was thinking about making a system which would alow you to use as many layers as you need (in editor you simply add new layers when needed). And if SpriteEngine is capable of working with thextures which also support Alpha chanell we might use so caled texture layering where you combine two textures to gain desired end effect. For instance:
Layer1: Contains simple texture which basically defines the overal colour of the water.
Layer2: Contains texture which has some parts semitransparent. This texture also controls water animation.
Layer3: Contains land texture. Part of this texture is transparent so water texture from below can be seen (shoreline).
So now we only need to figure out how much will each layer drop the overal performance (more layers more work for Graphics engine).

As for controlling the scroll speed I was thinking about storing the information about scroll speed in the TLine itself so that when you create specific line of sprites you also check if scroll speed should be adjusted.
Actually I was thinking about proposing you to implement some ingame event system with which you could contoll enemy spawning, maybe even change in enemy AI, scroll speed, and even controll sprites animations.
And when I'm talking about controlling sprite animations I have idea of implementing system which would alow you to easily synchronize animation between varios tiles and alow full controll on theese animations (stop them at any time, set them to specific position, change their speed, etc).
So you could have something like this in your game: Destroying a power statkion on an island causes all the lights to go blank, radars to stop turning, defensive turets to stop fireing at you etc. Posibilities are almost endles.

Yes yes implementing all theese ideas won't be an easy task but I'm sure it would be woth the efford in the end. And yes I'm preapred to give you a hand in all this now that I have development platform ready.

Wait when I'm gonna work my own projects ???

robert83
18-01-2013, 10:12 PM
Yeaaaa!!! :) , THANK YOU VERY VERY VERY MUCH!!!

I've modified my TEST button a bit


procedure TMain.Button7Click(Sender: TObject);
var i: integer;
begin
for i:=0 to SpriteEngine.Count-1 do
begin
if SpriteEngine.Items[i].ImageName <> 'selection.image' then
begin
SpriteEngine.Items[i].Dead;
end;
end;
if StartDemo = false then
begin
StartDemo:=true;
end
else
begin
StartDemo:=false;
RanOnce:=false;
CreateSprites;
DrawSprites;
end;
end;



Also modified the Scollmap procedure :


if offset in [0..15] then offset:=Map.map_length-15; // set offset to map end - one screen of tiles (0..14)


This way if I press the test button and I'm not on the first screen +1 line... I can start and stop the scrolling, then start it again from same position... think this is more usefull for the editor, say I feel something is off does not go well with the rest of the map I can stop it right there edit...and then continue the scrollmap .

For the BAD news , where to I do that ? on Form.Create ?
Is this that DANGEROUS thing ?


AsphyreDevice1.Height:=480; // Asphyre Height
AsphyreDevice1.Width:=640; // Asphyre Width

AsphyreDevice1.WindowHandle:=Panel1.Handle; // Asphyre uses Panel1 as rendering surface
AsphyreDevice1.Initialize; // Initialization
if AsphyreDevice1.Initialized = false then // if failes displays message and terminates program
begin
ShowMessage('Error initializing Asphyre!');
Close; // <---- if the the initialization fails , program closes
end;


ASDB1.FileName:=ExtractFilePath(Application.ExeNam e)+'TileSet.asdb'; // path to databse ( tilesets )
SpriteEngine := TSpriteEngine.Create; // Creates the SpriteEngine
SpriteEngine.Image:= Images; // Sets SpriteEngine to use Image source
SpriteEngine.Canvas:= AsphyreCanvas1; // Sets SpriteEngine to use AsphyreCanvas
Selection := TSelection.Create(SpriteEngine); // Creates the Selection Squire


For the speed I'm not quiet sure how should I add it to this ? Quote: "As for controlling the scroll speed I was thinking about storing the information about scroll speed in the TLine itself so that when you create specific line of sprites you also check if scroll speed should be adjusted."


TMap=record
name:string[20]; // Name of the Map
map_length:integer; // Length of the Map
Lines: array of TMapLine; <--- I should add here somehow the Speed , but how?
end;


As for the Layers ... the one you mentioned makes sense... but I don't even know where to begin . Texture layering sounds good, only problem is I have no idea how to implement.

I would probably have to change my Map record a bit...


TTileData=record
ImageIndex: integer; // Tile Number
AnimStart: integer; // Tile Animation Start Position
AnimSpeed: Single; // Tile Animation Speed
AnimCount: Integer; // Tile Animation Count
end;
TMapLine=array [0..19] of TTileData; // Stores one line of tile data
TLayers=array of TMapline; // stores all layers of Mapline
TAnimTable=array of array [0..19] of single;
TMap=record
name:string[20]; // Name of the Map
map_length:integer; // Length of the Map
Layers: array of TLayers; // stores each layer of the Entire map [0,0..19] , first value is 0 till map_length (vertical), second value is 0..19 (horizontal)
end;



Now I'm really starting to get lost in them Arrays, will have to start drawing I guess ....

Now if I understood you correctly , we might get away with somehow only using 320 sprite's and adding to them all the layers? (1,2,3,4,5,6...n) (instead of creating 320 sprites for each layer ) .
Unfortunately I don't know how to begin, how can I later say edit only layer2 sprites even if I have layer3,layer4 or layern on "top" of it . Was trying to google (damn Asphyre ... always brings up ACER laptops, and only a few valuable hits...)

Greetings
Robert

SilverWarior
19-01-2013, 01:37 AM
For the BAD news , where to I do that ? on Form.Create ?
Is this that DANGEROUS thing ?


AsphyreDevice1.Height:=480; // Asphyre Height
AsphyreDevice1.Width:=640; // Asphyre Width

AsphyreDevice1.WindowHandle:=Panel1.Handle; // Asphyre uses Panel1 as rendering surface
AsphyreDevice1.Initialize; // Initialization
if AsphyreDevice1.Initialized = false then // if failes displays message and terminates program
begin
ShowMessage('Error initializing Asphyre!');
Close; // <---- if the the initialization fails , program closes
end;


ASDB1.FileName:=ExtractFilePath(Application.ExeNam e)+'TileSet.asdb'; // path to databse ( tilesets )
SpriteEngine := TSpriteEngine.Create; // Creates the SpriteEngine
SpriteEngine.Image:= Images; // Sets SpriteEngine to use Image source
SpriteEngine.Canvas:= AsphyreCanvas1; // Sets SpriteEngine to use AsphyreCanvas
Selection := TSelection.Create(SpriteEngine); // Creates the Selection Squire



Yes. For some reason Close doesn't end the Application execution when expected. Usually you use Application.Terminate instead.
Also if you take a look at TAsphyreDevice component you will notice that it has OnInitialize event. That event can be used for aditional initialization procedures. So you can move all the stuff needed for SpriteEngine initialization into that even procedure.


For the speed I'm not quiet sure how should I add it to this ? Quote: "As for controlling the scroll speed I was thinking about storing the information about scroll speed in the TLine itself so that when you create specific line of sprites you also check if scroll speed should be adjusted."

I was thinking of adding another variable in TMapLine record.


As for the Layers ... the one you mentioned makes sense... but I don't even know where to begin . Texture layering sounds good, only problem is I have no idea how to implement.

I would probably have to change my Map record a bit...

I was thinking of redesignign Map record entirely but haven't figured out how exactly yet.


Now I'm really starting to get lost in them Arrays, will have to start drawing I guess ....

Yes multidimensional arrays can be a bit puzeling. All you have to do is learn how to think out of the box to fully understand them.
This might help you a bit:
Records (TTileData) are sheets of paper.
Lines (TMapLine) are books containing this sheets of paper.
Layers are shelves on which you have all theese books.
Map(TMap) is a library with all theese shelves.



Now if I understood you correctly , we might get away with somehow only using 320 sprite's and adding to them all the layers? (1,2,3,4,5,6...n) (instead of creating 320 sprites for each layer ) .

Unfortunately we would need to create specific sprites for each layer. Good news is that upper layers won't need to have full grid of sprites but would have sprites only on certain positions so final count of sprites wont be 320 * number of layers.


Unfortunately I don't know how to begin, how can I later say edit only layer2 sprites even if I have layer3,layer4 or layern on "top" of it . Was trying to google (damn Asphyre ... always brings up ACER laptops, and only a few valuable hits...)

From what I see you manually calculate map position which needs to be updated on mouse click. That is good as you only need to add additional condition which determines which layer are you editing.


Anywhay I have decided to go and redesign map record so it will support multiple layers.
I plan on designing layers in a whay so that they can have varios sizes, both in lenght as in sprite sizes used in them (hopefully it would alow us to implement paralax scrolling).
I would redesign scrolling procedure so it would be compatible with new layer types.
I would need to design map file routine to support storing all this information in organized manner (maped file approach). Don't wory I will also write short documentation explaining how this works so you could do futher changes in the future.

I also plan on adding some other tweaks to your code.
For instacne you are currently calculating your mouse position inside AsphyreTimer1.Process event procedure while you could easily just use TPanels OnMouseMove event to do so.
And to determine if mouse is over panel you could simply use TPanels OnMouseEnter and OnMouseExit events to detect when mouse is moved over panel and when away from it. The best thing about theese events is that they also detect if mouse moves over some other window which is positioned above your Panel.
EDIT: OnMouseEnter and OnMouseExit events are unfortunately not present in Delphi 7.

robert83
19-01-2013, 10:29 AM
Hello,

I'll change that to terminate , and move Sprite Engine initialization into that part.
Well my uncle has a Panel that he created and has onmouseenter and onmouseexit, I'll ask him for his permision to give it to you. That way we can start using that other panel, and redesign the mouse handling as well . Would be a lot more simple .

Greetings
Robert

SilverWarior
19-01-2013, 01:52 PM
Well my uncle has a Panel that he created and has onmouseenter and onmouseexit, I'll ask him for his permision to give it to you. That way we can start using that other panel, and redesign the mouse handling as well . Would be a lot more simple .

Yes you can ask him for that. But even if he doesn't agree it would be no big problem as I was already suggesting similar aproach to someone else in the past I just have to remember to whom I have been suggesting this. Maybe I even have code for this somwhere on my computer.

BTW after some fideling with your code I actually decided to go and start from scratch as there are some parts of your code which are not clear to me. This seems easier to me. I also decided to use a bit different approach which might be a bit more difficult to understand but it should provide us with more options in the end.

robert83
19-01-2013, 08:26 PM
Hello,

Asked my uncle, gave me permission to post the AthorPanel pas,dcu,dcr here for your convinience. It has onmouse enter onmouse leave.


I'm working further on my editor, even tough you've said that you'll do it from scrach ( I'm doing it for the sake of learning, man gotta do mistakes to learn ).

I've changed the TileData ImageIndex to ImageName string[10] , why I did this? Well I burned myself, I was editing the asdb file... and don't know how I managed , I've deleted the last entry , added it again, then was doing something
removed last entry added again, and the entire index of the asdb file went to hell.... my map's were using tiles but of incorrect index, thus I'm using names instead of index numbers. Did not notice any speed hit on my comp. Will post it later.

(Note : I also managed to get my hands earlier on some level editor made by someone on the Asphyre Forums... it also stores the imagename instead of imageindex , probably for same reason...safer , the name never changes the index might)

Greetings
Robert

ps.: attached the component

robert83
22-01-2013, 07:33 PM
Hello,

I was drawing tiles for the last few days...damn it sure takes a lot of time...and then it's still , I don't know , not good enough.

Anyway today I've decided to modify the mouse cursor thing, and I was playing with the AthorPanel... and I've realized, that we don't even need , on mouse enter or on mouse leave, or MyMouse valid for that matter, this is what I did :



procedure TMain.Panel1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if (MyMouse.Position.X < Panel1.Width) and (MyMouse.Position.Y < Panel1.Height) then // Mouse can only go till edge of Panel1
begin
MyMouse.Position:=Mouse.CursorPos; // I get mouse cursor position
dec(myMouse.Position.X, Left+Panel1.Left+Panel2.Width); // I remove from the cursor position
dec(myMouse.Position.Y, Top+Panel1.Top+Panel4.Height);
end
else
begin
MyMouse.Position.X:=MyMouse.Position.X-1; // once it reaches edge , in order not to get stuck... I decrease it's value by 1
MyMouse.Position.Y:=MyMouse.Position.Y-1;
end;
end;



And on AsphyreDevice render



// get position on map, for example 2;4 , but only if x < 20 and y < 15 , the myMouse.valid was not enough to prevent it from happening
if (floor(myMouse.Position.X / (Panel1.Width / 20 )) < 20) and (floor(myMouse.Position.Y / (Panel1.Height / 15 )) < 15 ) then
begin
map_x_pos:=floor(myMouse.Position.X / (Panel1.Width / 20 ));
map_y_pos:=floor(myMouse.Position.Y / (Panel1.Height / 15 ));
Label16.Caption:=inttostr(map_x_pos)+','+inttostr( map_y_pos);
with Selection do
begin
X:=map_x_pos*32;
Y:=map_y_pos*32;
end;
end;


Does the same thing, + better , this way I NEVER-EVER leave the grid, sometimes with previous versions it was possible to leave the grid... the Selection cursor dissapeared, and your position was X:=20, or Y:=15 for example.

Greetings
Robert

robert83
23-01-2013, 11:44 AM
Damn,

My last post was heavily bugged, damn, I was using Panel OnMouse move, and I don't know how the heck did I not see it provides X,Y...


procedure TMain.Panel1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
MyMouse.Position.X:=X;
MyMouse.Position.Y:=Y;
end;


On Device Render


map_x_pos:=floor(myMouse.Position.X / (Panel1.Width / 20 ));
map_y_pos:=floor(myMouse.Position.Y / (Panel1.Height / 15 ));
Label16.Caption:=inttostr(map_x_pos)+','+inttostr( map_y_pos);
with Selection do
begin
X:=map_x_pos*32;
Y:=map_y_pos*32;
end;


Now I only need to make the logic if I keep the mouse pressed it should draw the last tile... say I wanna draw land tiles for say 20 screens, it wont do clicking one-by-one.

Greetings
Robert

User137
23-01-2013, 01:20 PM
Now I only need to make the logic if I keep the mouse pressed it should draw the last tile... say I wanna draw land tiles for say 20 screens, it wont do clicking one-by-one.
Notice that Shift is also provided. It can tell if left mousebutton is down. Just throwing some code, might or might not be what you are after ::)

procedure TMain.Panel1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
MyMouse.Position.X:=X;
MyMouse.Position.Y:=Y;
ScreenToWorldCoords(X, Y);
if ssLeft in Shift then begin
FillTile(trunc(worldX), trunc(worldY), Pattern);
end;
end;

robert83
24-01-2013, 09:25 AM
Hello,

Thank you! I did it. For the mouse click part I mean.

Anyway I was going a bit further as per SilverWarrior's ideas, my next step is the grid where I can have different sized tiles . I made a drawing about how I thought I'd make it.

My thoughts :

1. I was looking at multiple TILE based editors and all of them had some sorta base tile , which I think if I'm right is the MINIMUM tile size possible on the map, say 32x32 is minimum, but I can have up to say 256x256 tiles for example.
2. Even though I will have multiple sized tiles , and the possiblity that say layer 15 will only have like 10 entires , I'm still sticking to the dynamic array right ? ( probably I don't want to go there right now, ... but how much memory am I
"wasting" with a layer 15 array that is 20x150 and has only 10 entries , meaning the other 2990 are there for nothing , is Delphi "smart" here and is not allocating memory for non used array entries?
3. With this idea I would put images larger then 32x32 into the ASDB file like this , 128x128 example : Texture Size : 128x128, Pattern Size : 32x32 ... when I would put the image into the array I would use a for cycle to put the apropriate
Patterns into the apropriate array entries... something like this :


procedure TMain.PlaceTile;
var x,y,pattern : integer;
begin
pattern :=1; // here I reset pattern count to 1
If EditorInitialized = true then
begin
for y:= map_y_pos to (Images.Image[ListView1.Items[ListView1.ItemIndex].Caption].Size.Y div Images.Image[ListView1.Items[ListView1.ItemIndex].Caption].PatternSize.Y) do // I go from cursor position till image bottom
begin
for x:= map_x_pos to (Images.Image[ListView1.Items[ListView1.ItemIndex].Caption].Size.X div Images.Image[ListView1.Items[ListView1.ItemIndex].Caption].PatternSize.X) do // I go from cursor position till image right edge
begin
map.Lines[y+Offset,x].ImageName:=ListView1.Items[ListView1.ItemIndex].Caption; // I need to know image name (will probably revert back to Index )
map.Lines[y+Offset,x].PatternNo:=pattern; // I need to know pattern index
inc(pattern,1);
end;
end;

(the above code is partly from head , so there is a probability that It will not to what I think it should do :) , will test it later though )
This way I would still need to draw Sprites for one screen , but could have any tile sizes in them above 32x32 till say 256x256 ( 512x512 to big... because my screen is 640x480 for this project , I don't want to overwhelm myself )
With minor adjustment my current draw procedure could handle this and the scrollmap procedure as well (atleast I think so... did not go start fidling with the code itself...)

The only setback which is probably a huge one, that I need to create full sprite engine sheet for each layer LayerN * 301 .

UPDATE : I need to change my AnimCount to take data from TextureCount instead of PatternCount ( was wrong in using PatternCount in the first place probably...) , I tested this when adding a 128x128 file to the
asdb database , 128x128 Tile, PatternCount 6, Texture Count 1 ....

UDPATE 2 : another error I made everywhere, I've just noticed that I must make my textures with sizes like this : 128x128 ( here I have 1 texture, 16 patterns) then 128x256 (here I have 2 texture , 32 patterns ) , if I do it like this 256x128 ... I get mixed up lines...
and if I make an animation I must place every new texture bellow the original, not like I'm doing it now to the right....

--------------------------------------------------------------------------------------------------------------------------

My other idea was, based on a LevelEditor found on the Asphyre forums (the old one...) , is to create the Sprite 's on demand, but my problem is I don't know how could I mix that combo with a dynamic array. In that editor the user clicks
anywhere on the map, and a tile is created in that position, with apropriate size (be it 32x32, 128x128, 256x256....) , the only thing is that , that map editor does not use array to store map data it uses LevelStruct record and it writes it to a file of TLevelStruct and reads it that way. Which is fine but I have a few problems with it I don't know how to solve :

1. I place a tile on the map with Z:=0 , I place another tile on top of this Z:=1 , Z:=2 , Z:=3 ... without the array how do I tell the program that I want to delete (replace) only Tile at Z:=1 ? The LevelEditor uses OnCollision to detect if the cursor is on a certain sprite, but I don't know how well it works if I have in worst case scenario tiles that overlap.... ground bellow, then flowers, and tree above for example.
2. Also with this Editor, without the array I need to create all the sprites ... ( side note : or maybe I could read them from the file line by line ? use the file , kinda like an array ? hmmm....)
3. Scrolling I think would not be problematic.... I could scroll same way and destroy the Sprite if it's Top is out of the screen ( example tree 128x128, I kill it if it's completely outside of screen )

I'm not sure which way to go, anyway I thought I'd share my ideas here, for you to comment and for me to remember, right now I'm kinda stuck.

Greetings
Robert

User137
24-01-2013, 12:04 PM
You only need 1 patternwidth,height for 1 image, in your case that 32x32 can be fine. If you need a big 200x190 odd sized sprite, you can use a separate image for it. But if you want to use the 32x32 image for bigger combinations, you don't need anything special for the drawing engine. Plotter would just insert in 64x64 sprite case all 4 of those 32x32 patterns to world tile grid. That is only the editor application's concern. I know it's not going to be too easy, to program editor to behave like that. Have to make sort of map file for each pattern image, to describe the areas.

There is another sort of "areas" in tiles too:
- Random variations of each tile.
- Turning tiles... For example when making room, you can have 16 different kind of patterns (straight vertical, straight horizontal, 4 corners, 4 ends, 1 lonely tile, 1 tile surrounded by 4, and 4 "T" sections). Most tile editors go with the lazy way, and tell map maker to select the different tile himself. But smart program tells you to use just 1 tile, and paint the whole wall with it, selecting the right pattern automatically. I have done this before, it's complicated but i can give 1 sort of hint:
Pattern description for top left corner could be:

?xx
x##
x#?
...where (? is any pattern, # is same pattern and x is different than #). You can make similar filter for every pattern type, for automatic selection.

robert83
24-01-2013, 03:11 PM
I'm trying to implement the following correctly :

Put 32,64,128,256 x 32,64,128,256 Sized Tiles on my Map... right now I can put it on map... but it's not handled correctly because in my Grid I assume each tile is 32x32... so if I create a 64x64 tile , the following happens [see image for better understanding]

Array is updated but only the top,left corner has the data for the tile... while the other 3 have no data... and for example if I scroll down...and the first tile is not visible the entire 64x64 tile dissapears, or if I try to place a regular 32x32 tile to it's bottom right corner it wont be
visible ...


I think I should somehow maybe draw parts of the image ?

0,0 first part , 0,1 second part
1,0 third part, 1,1 fourth part

But I don't know how to do this ... (or for that matter if it's the right move)

Inside the asdb database the image I'm playing right now with is 64x64 in size.

Greetings
Robert

SilverWarior
24-01-2013, 08:08 PM
Robert first of all I'm sorry that I haven't done much work in the past few days but I do have some real life isues at the moment.


Anyway I was going a bit further as per SilverWarrior's ideas, my next step is the grid where I can have different sized tiles . I made a drawing about how I thought I'd make it.

My idea was that different layers use different tile sizes but still each layer can only contain tiles of the same size. For instance you have layer1 with tiles 32x32 in size but layer2 has tiles 64x64 in size. None of the layers actually has 32x32 and 64x64 sized tiles at the same time.



Even though I will have multiple sized tiles , and the possiblity that say layer 15 will only have like 10 entires , I'm still sticking to the dynamic array right ? ( probably I don't want to go there right now, ... but how much memory am I
"wasting" with a layer 15 array that is 20x150 and has only 10 entries , meaning the other 2990 are there for nothing , is Delphi "smart" here and is not allocating memory for non used array entries?

Array is just one continuos block of memory containing all the data of its items.
The only difference between dynamic and static array is that static array has fixed size and mumber of items it can contain right after its creation while dynamic array can be resized at runtime. S
Memory requred for each of theese array types is number of array items multiplied by item size.



The only setback which is probably a huge one, that I need to create full sprite engine sheet for each layer LayerN * 301 .

Not necessarely. You create only those sprites that are needed. It would be useles creating empty sprites in upper layers.


Anywhay I plan on replacing your arrays of records with Lists and objects. Why?
List is actually just a managed array of pointers. Theese pointers then point to object, varibales or, records of data to be used. In our case I was planning of using TObject list which only contains pointers to TObjects.
So mapdata would actually be stored in bunch of objects which would replace TTile record. Then you would have one TObjectList for each line and finally one TObjectList for each layer.
As list only use pointers you can actually have list with 100 of items where only 10 of them point to 10 different objects and rest of them are set to nil.
This would mean that in this case you would use 100 * 32 bit of data for storing TObjectLists data and 10 * size of object to store actually data required for needed tiles.
And the best thing is that you can actually have several TObjectList items to actually point to the same object meaning that you would actually need only so many object as the number of posible tiles.
So if your game have 100 of different textures you would only need 100 of objects even if your map would have 100 tiles width and 10000 tiles of height. Actually in above example you would need:
100 objects
10000 of TObjectList storing data for each line while each of this TObjectLists would have 100 items (pointers) to point to necessar data for each line.
and one object list which would actually point to those other TObjectLists representing data for each line.
And if one of your layers don't need any tile rendered in certain line this means that you won't even need TObjectList representing data to that line and TObjectList representing whole map data would have one boject which would othervise point to that line data set to nil.

I hope I will have more time in the next few days to put all this into code. I will try to do this in a way so that it would be easily used as posible and ofcourse write a documentation thourally explaining how this actually works.

User137
24-01-2013, 08:47 PM
Class objects (TObject) are way of making code "object oriented", or sometimes more readable for us humans. However they have a drawback that it will take more memory, and be slower than traditional arrays, records and pointers.
1 TObject takes some memory. First it has the reference "pointer" which is 4 or maybe 8 bytes on 64-bit systems. Then there is TObject's internal data, and finally user data.

If i'd have to make tile-based game with 10000 different textures (absurd in first place, because lots texture switching puts huge stress on rendering speed), i would have tile indexes in a dynamic array, and another array just for the information for each index. Like:

type
TIndexInfo = record
pattern, texture: word;
end;
PIndexInfo = ^TIndexInfo;

TTile = packed record
index: word;
end;

indexInfo: array of TIndexInfo;
tiles: array of TTile;

function GetTileInfo(x, y: integer): PIndexInfo;
begin
if ...x, y in valid range... then // range check if possible to call this outside of map boundaries
result:=@indexInfo[tiles[x+y*MapWidth].index];
end;
So each tile takes 2 bytes of memory, if you don't define other tile specifics into TTile. indexInfo array must be initialized at program start too, for each texture and pattern present.

SilverWarior
24-01-2013, 11:08 PM
If i'd have to make tile-based game with 10000 different textures (absurd in first place, because lots texture switching puts huge stress on rendering speed), i would have tile indexes in a dynamic array, and another array just for the information for each index. Like:

I wasn't talking about having 10000 different textures (who would take the time to make all theese) but instad I was talking about having the map size where its height is 10000 tiles (map width is othervise 19 tiles if I recal corectly).
I was actually talking about having 100 different posible textures and not 10000.



TObject takes some memory. First it has the reference "pointer" which is 4 or maybe 8 bytes on 64-bit systems. Then there is TObject's internal data, and finally user data.

I'm aware of that.
The reason why I plan to use TObjects is to gain advantage of using its properties. If you recal we also talked about the way of syncrhronizing tile animations. I plan on doing this in a way that each TObject which would be representing certain tile would also contain pointer to another TObject which would represent animation information (animation lenght, speed, etc).
Sure you could do this using records but then you need to have code similar to this:

Tile.Animation.Speed
But I was thinking of doing this something like this.

type
TAnimation = class(TObject)
private
FSpeed: Single; //Defining animation speed
FCount: Integer; //Defining the number of frames animation has
FOffset: Integer; //Defining on which frame does animation starts
FPosition: Integer; //Defining current animatiomn position
protected
procedure SetSpeed(AValue: Single);
procedure SetCount(AValue: Integer);
procedure SetOffset(AValue: Integer);
procedure SetPosition(AValue: Integer);
public
property Speed: Single read FSPeed write SetSpeed;
property Count: Integer read FCount write SetCount;
property Offset: Integer read FOffset write SetOffset;
property Position: Integer read FPosition write SetPosition;
end;
TTile = class(TObject)
private
FTexture: Integer; //Defining which image to use
FAnimationParam: TAnimation; //Object storing animation parameters
protected
procedure SetTexture(AValue: Integer);
procedure SetAnimationParam(AValue: TAnimation);
function IsAnimated: Boolean;
function GetAnimationParam: TAnimation;
function GetAnimationSpeed: Single;
function GetAnimationCount: Integer;
function GetAnimationOffset: Integer;
function GetAnimationPosition: Integer;
public
property Texture: Integer read FTexture write SetTexture;
property AnimationParameters: TAnimation read GetAnimationParam write SetAnimationParam;
property AnimationSpeed: Single read GetAnimationSpeed;
property AnimationCount: Integer read GetAnimationCount;
property AnimationOffset: Integer read GetAnimationOffset;
property AnimationPosition: Integer read GetAnimationPosition;
property Animated: Boolean read IsAnimated;
end;



function TTile.GetAnimationParam: TAnimation;
begin
result := nil;
if FAnimationParam <> nil then
result := FAnimationParam;
end;
function TTile.GetAnimationPosition: Integer;
begin
if FAnimationParam <> nil then
result := FAnimationParam.Position;
end;

function TTile.IsAnimated: Boolean;
begin
result := False;
if FAnimationParam <> nil then result := true;
end;

procedure TTile.SetAnimationParam(AValue: TAnimation);
begin
FAnimationParam := AValue;
end;

NOTE: Code is writen from my head so it is posible to contain errors. Also it isn't a compleete - several class methods are still missing so it won't work as such.

robert83
25-01-2013, 08:03 PM
Thank you guys, obviously I misunderstood , and since my knowledge is limited on this field, I've almost went on the wrong road, by trying to somehow enable the usage of different sized tiles on the same layer (probably possible, but is faaar beyond my current level I think, and for a first project I think I need not over complicate) . So I think once I figure out how to properly create only needed sprites with my current array setup... I'll try using that approach, layer 1 custom size 32x32 ,64x64,128x128 etc...

Oh and no problem , I'm very greatefull for you taking your time and replying giving great advices, help. Thank you!

Meanwhile since I could not sit idle.... I did a little further coding , added aditional layers , right now for my convinence I've made it fixed, I have 4 layers... I can edit, all of the layers have 310 sprites... which I later tend to fix, I just wanted to make something work, so I can feel like, I did something.

Anyway I'm attaching the source, if you'll take a look, you probably notice a lot of commented lines in my scrollmap part. I've actually had a brainstorm, and realized how I can create only needed tiles, I even went as far as to only move
existing tiles in the array , my only problem is as you see, since I'm only moving Tiles that are not NIL, I'm also limiting myself to creating tiles that were not nil previously... , will need to rethink my logic there, but I'm think I'm close to solving the problem to only display as many sprites as I need, not 4*310. Also I've noticed one strange thing here, I need to specificaly go trough the array and NIL those entires that have no BackGround Sprite... otherwise something is left there which is not a BackGround Sprite, but is not NIL either. Probably gona have to watch out for this in the future, and do a for cycle to clean dynamic arrays with NIL .

I've also fixed the mouse, it's now properly implemented, tough it has one problem which I will fix later... I'm allowing user to place tiles when the mouse button is pressed down on panel move... this works great...but is executing the command to many times uneaded... I will need to probably get current mouse position store it, and then only place new tile and update the screen if I moved mouse from previous position in either direction by tile width or height (if not 32x32, but 64x32 or something else).

I've also changed to use TMemoryStream instead of TFileStream. And fixed a few possible bugs in the SaveLevel and LoadLevel procedure , sizes were not set properly...

One HUGE bug I have is, if you click on the TListView where is no TILE and then CLICK on the MAP , you get an access violation, will look into this later.

The tileset I'm using right now is not mine, it's from a free site providing awesome tiles.

Attached my project full source + map included, you can load the map, and then press the test button to test it.

In the meanwhile I'm gona look up and study TObjectList , TList , I've actually heard of it in another TILE MAP editor howto for BASIC I think pretty old one... the guy who wrote it mentioned that it's smarter to use ObjecList because of
the fact that storing layers on array is a waste of space , because not all layers will actually be completely filled, infact probaly none of the layers will be 100% filled, maybe the first...


Greetings
Robert

SilverWarior
26-01-2013, 12:52 AM
Thank you guys, obviously I misunderstood , and since my knowledge is limited on this field, I've almost went on the wrong road, by trying to somehow enable the usage of different sized tiles on the same layer (probably possible, but is faaar beyond my current level I think, and for a first project I think I need not over complicate)

Actually you would probbably want to have athleast one layer which would have tiles of different sizes (enemies) but you won't try to put them all into grid as you did before. :D



Meanwhile since I could not sit idle.... I did a little further coding , added aditional layers , right now for my convinence I've made it fixed, I have 4 layers... I can edit, all of the layers have 310 sprites... which I later tend to fix, I just wanted to make something work, so I can feel like, I did something.

I'm glad to see your entuziazm to continue your work and learn.



Also I've noticed one strange thing here, I need to specificaly go trough the array and NIL those entires that have no BackGround Sprite... otherwise something is left there which is not a BackGround Sprite, but is not NIL either. Probably gona have to watch out for this in the future, and do a for cycle to clean dynamic arrays with NIL .

Yes you need to set array items to nil othervise they can have random values. These values are actually residual data left in memory probably by some other function.
When you create array your program reserves a block of memory with required size but some memory within this block might have been used in past and then freed (become available). This results in array items to have unusual almost random values.



I've also fixed the mouse, it's now properly implemented, tough it has one problem which I will fix later... I'm allowing user to place tiles when the mouse button is pressed down on panel move... this works great...but is executing the command to many times uneaded... I will need to probably get current mouse position store it, and then only place new tile and update the screen if I moved mouse from previous position in either direction by tile width or height (if not 32x32, but 64x32 or something else).

Easiest way to fix this is that you use X,Y values to calculate the position on the layer (where would be selection moved to) and then check if your selection isn't already at that position. If it is then you just move on (no need to updating it) but if it isn't you go and update the selection position and change desired tile.



I've also changed to use TMemoryStream instead of TFileStream.

May I ask why (sorry haven't checked your code)?
Doing so you load whole map file into memory and then use it to populate your dynamic arrays which in the end only means that during this procedure you are using twice the amount of memory.
Using TMemoryStream instead of TFileStream when saving data might actually gain a bit of performance as HDDs are not fast when making small and rapid changes to files.
But reading from files this way doesn't provide you with any advantages unles you have this file stored somwhere on network drive which you probbably don't.



One HUGE bug I have is, if you click on the TListView where is no TILE and then CLICK on the MAP , you get an access violation, will look into this later.

I don't recal how exactly does this part of your code work but when there is no item selected in TListView it is returning ItemIndex of -1. So you could make sure that you select some default item in it when ItemIndex is set to -1 (using OnChange event).

robert83
08-02-2013, 07:52 PM
Hi,

Finally did it , after much-much-much trial and error, and experimenting with stuff, in the end I was moving code around cause I was totally stuck. I am now only creating Sprites that are needed, and I'm doing it so that now I have only
one procedure to draw sprites when moving around the editor instead of the two I had earlier... and I think it's much much better (that is If what I think is correct...).
1. Create Needed Sprites only
2. If Sprite is already Created only update image property
3. Uneaded Sprites are terminated .

The procedure for SPRITE CREATION on MAP MOVEMENT :



procedure TMain.CreateSprites;
var x,y,z : integer;
begin
for z := 0 to 3 do // 4 Layers
begin
for y := 0 to 14 do // 15 Lines
begin
for x := 0 to 19 do // 20 Columns
begin
if map.Layer[z].Lines[y+Offset,x].ImageIndex <> 0 then // Create/Modify sprites which have something to show
begin
if BackGround[z,x,y] = nil then // If sprite was not created I create it
begin
BackGround[z,x,y] := TBackGround.Create(SpriteEngine);
BackGround[z,x,y].ImageName:= Images.Item[map.Layer[z].Lines[y+Offset,x].ImageIndex].Name;
BackGround[z,x,y].X:=x*32;
BackGround[z,x,y].Y:=y*32;
BackGround[z,x,y].Z:=z;
end
else
begin // otherwise I only set new image
BackGround[z,x,y].ImageName:= Images.Item[map.Layer[z].Lines[y+Offset,x].ImageIndex].Name;
end;
// Animation properties
if BackGround[z,x,y].PatternCount = 1 then
begin
BackGround[z,x,y].DoAnimate:=false;
end
else
begin
BackGround[z,x,y].AnimStart:=0;
BackGround[z,x,y].AnimCount:=map.Layer[z].Lines[y+Offset,x].AnimCount;
BackGround[z,x,y].AnimSpeed:=map.Layer[z].Lines[y+Offset,x].AnimSpeed;
BackGround[z,x,y].AnimPos:=(GlobalAnimCounter mod BackGround[z,x,y].AnimCount)+map.Layer[z].Lines[y+Offset,x].AnimStart;
BackGround[z,x,y].DoAnimate:=true;
BackGround[z,x,y].AnimLooped:=true;
end;
end
else // if Sprite is empty, I kill it , and set the Array entry to NIL
begin
if BackGround[z,x,y] <> nil then
begin
BackGround[z,x,y].Dead;
BackGround[z,x,y]:=Nil;
end;
end;
end; // x loop
end; // y loop
end; // z loop
end;





The ScrollMap procedure is also a lot more simple :



procedure TMain.ScrollMap;
var x,y,z: integer;
begin
// I re-create all the sprite's here , because it was having problems using the ones from the CreateSprite() and DrawSprite procedures
if ranonce = false then
begin
SetLength(AnimTable, map.map_length); // Here I set the length of the array that is responsible for calculating all animation positions for each sprite
AnimSync:=true; // This way I'm making all my newly created sprites regardles of their creation time in sync with every other sprite
if offset in [0..15] then offset:=Map.map_length-15; // set offset to map end - one screen of tiles (0..14)
CreateSprites; // I create/modify Sprites at position given
ranonce:=true; // won't run again ... only if I reach the end of the map , and start all over again.
end;

if Offset > 0 then
begin
Counter:=Counter+Speed;
if Counter = 32 then
begin
Dec(Offset,1);
Edit9.Text:=inttostr(Offset);
Counter:=0;
end;

for z:=0 to 3 do
begin
for y:=0 to 15 do
begin
for x:=0 to 19 do
begin
if Offset > 0 then
begin
if map.Layer[z].Lines[y+Offset-1,x].ImageIndex <> 0 then
begin
if map.Layer[z].Lines[Offset-1,x].ImageIndex <> 0 then
begin
if BackGround[z,x,y] = nil then
begin
BackGround[z,x,y] := TBackGround.Create(SpriteEngine);
BackGround[z,x,y].ImageName:= Images.Item[map.Layer[z].Lines[Offset-1,x].ImageIndex].Name;
BackGround[z,x,y].X:=x*32;
BackGround[z,x,y].Y:=-32;
BackGround[z,x,y].Z:=z;
end;
end;
end;
end;
if BackGround[z,x,y] <> nil then
begin
BackGround[z,x,y].Y:=BackGround[z,x,y].Y+Speed;
if BackGround[z,x,y].Y = 480 then
begin
BackGround[z,x,y].Dead;
BackGround[z,x,y]:=Nil;
end;
end;

end; // X loop
end; // Y loop
end; // Z loop
end; // if Offset



I was playing around a lot with ideas in my head till I finally came to this solution , wrote the code alone, but a bit lost at the if parts , since I did not imediatelly comment why I did it... I don't know right now why I did this :)


if map.Layer[z].Lines[y+Offset-1,x].ImageIndex <> 0 then
begin
if map.Layer[z].Lines[Offset-1,x].ImageIndex <> 0 then
begin


But one does not work without the other... I don't understand why I need the very first if..., but if it's not present it's not working correctly , for example if the map contains at parts no Sprites at all. ( the black screen was intentional
to test the editor... what happens if...)

Anyway attached is the source code , with a map . I'm resting a bit now, cause in the last few days... I was basically banging my head against the wall to solve this damn "smart" drawing , scrolling of Sprites .

Also changed a few stuff in the code, forgot what exactly, but it's cleaner now.


My next step is :

Make each created map use it's own ASBD file with different tilesets.

I've come to this conclusion because I think I can even create my Menu , and some introes (moving pictures, kinda like Halloween Harry, only not so good pictures :) ) as well with this map editor ( or maybe it will be a game editor finally.... )

Also maybe I'll start another thread, since this is a long way from the original problem "Only Draw what is visible"

Sorry if my writing is a bit chaotic, I'm a bit overwhelmed. Working on some other program as well for the company I work in. But I'm also trying to do as much work on this Editor as possible...

Greetings
Robert

SilverWarior
09-02-2013, 12:51 AM
My next step is :

Make each created map use it's own ASBD file with different tilesets.

I've come to this conclusion because I think I can even create my Menu , and some introes (moving pictures, kinda like Halloween Harry, only not so good pictures :) ) as well with this map editor ( or maybe it will be a game editor finally.... )

Yes this is an interesting idea. But on the other hand it means that you might have some graphics saved in multiple places (two maps using same tileset) which would just increase the size of your game.
I was trying to implement similar approach by getting rid of ASBD database entirely but so far my idea didn't work. I'll have to figure out how exactly TAsphyreImages works (need to be able to conect certain images with certain index).



Also maybe I'll start another thread, since this is a long way from the original problem "Only Draw what is visible"

Actually I was planning to suggest you that we move this thread to MyProjects section of the forum and rename it. Why? This thread is great example of how development progresses in such project (we don't have many of theese here on the forums). Besides we have strayed from the main topic long time ago.
If you agree with my proposal just say so and please give me the new title of the tread (atleast the name you have chosen for your project) and I can do the rest since I'm a forum moderator.


Also I would like to apologize again for me not doing my part as I promised but I realy don't have any luck lately. Just when it seemed that I'll get some time I caught a nasty cold which put me in my bead for several days. Even now when it is better already I still feel like crap.

User137
09-02-2013, 03:17 AM
begin
for z := 0 to 3 do // 4 Layers
begin
for y := 0 to 14 do // 15 Lines
begin
for x := 0 to 19 do // 20 Columns
begin
if map.Layer[z].Lines[y+Offset,x].ImageIndex <> 0 then // Create/Modify sprites which have something to show
begin
if BackGround[z,x,y] = nil then // If sprite was not created I create it
begin
BackGround[z,x,y] := TBackGround.Create(SpriteEngine);
...
This is 11 indent levels. It's reaching critical levels where you are propably actually hurting the readability. If you want to add code after these blocks, you may be having hard time knowing which means which. Along the years i went along with this style:

begin
for z := 0 to 3 do begin // 4 Layers
for y := 0 to 14 do begin // 15 Lines
for x := 0 to 19 do begin // 20 Columns
if map.Layer[z].Lines[y+Offset,x].ImageIndex <> 0 then begin
// Create/Modify sprites which have something to show
if BackGround[z,x,y] = nil then begin// If sprite was not created I create it
BackGround[z,x,y] := TBackGround.Create(SpriteEngine);
...
It's only 6 levels, with very compact and clear structure, in my opinion at least. (Rest of the readibility can be added by configuring IDE fonts.)

phibermon
09-02-2013, 12:56 PM
if you're going to talk about saving lines, then remember you don't have to use a begin/end block with every for loop :





for z := 0 to 3 do
for y := 0 to 14 do
for x := 0 to 19 do
if map.Layer[z].Lines[y+Offset,x].ImageIndex <> 0 then
if BackGround[z,x,y] = nil then
begin
BackGround[z,x,y] := TBackGround.Create(SpriteEngine);
end;

User137
09-02-2013, 11:39 PM
if you're going to talk about saving lines, then remember you don't have to use a begin/end block with every for loop :
I know. Point was indents which you took away completely.

wagenheimer
10-02-2013, 01:15 AM
I got some errors with your editor... Invalid Float Value error... I think it is because here in Brazil we use , instead of . on float values. Should be easily fixable! =)

SilverWarior
10-02-2013, 01:45 AM
I got some errors with your editor... Invalid Float Value error... I think it is because here in Brazil we use , instead of . on float values. Should be easily fixable! =)

Yes this is becouse he is using standard edit boxes and then converts text into numerical values. I was planning on suggesting him to use value edit instead but I seemed to forgot about this.

robert83
10-02-2013, 12:03 PM
Yes this is becouse he is using standard edit boxes and then converts text into numerical values. I was planning on suggesting him to use value edit instead but I seemed to forgot about this.


Can you reccomend a good component for this ? (Delphi 7 has none, I was googling around found quiet a lot of them)

Found this, seems to work fine, was simple to install, nothing to fancy : http://www.delphiforfun.org/Programs/delphi_techniques/Numeric Edit Component.htm

I'm cleaning up the code a bit more, I'll think about a new title for this Thread and will inform you about it. Thank you very much!

Greetings
Robert

SilverWarior
10-02-2013, 03:08 PM
Delphi 7 has one it is caled SpinEdit. The component on itself is limited to only integer numbers. But since you are defining the animation in miliseconds you actually don't even need to use decimal points in the first place. This way you could even keep using regular edit. So instead of using 0.01 for 10ms delay you simply use 10 and then calculate which portion of the second this delay is dividing number from EditBox with 1000.

User137
11-02-2013, 01:05 AM
I tend to put:

DecimalSeparator:='.';
Then it's dot always and on everything, ignoring whatever language settings are.

I mean, wouldn't you just hate it if this application would on some computers write '.' and sometimes ','?

var d: single; F: TextFile;
begin
d:=1.001;
assignfile(F, 'test.txt');
rewrite(F);
writeln(F, d);
closefile(F);
end;
That would be very bad if you would want to send such files to others, with different language settings.

SilverWarior
11-02-2013, 12:50 PM
I tend to put:

DecimalSeparator:='.';
Then it's dot always and on everything, ignoring whatever language settings are.

Yes and when someone who comes from country where "." is used as thousand seperator and sees 1.001 thinks that this actually means thousand and one and not one point zero zero one.
The reason why Language settings are available to any application is so that each of theese aplaication can use proper settings when formating strings.


I mean, wouldn't you just hate it if this application would on some computers write '.' and sometimes ','?

var d: single; F: TextFile;
begin
d:=1.001;
assignfile(F, 'test.txt');
rewrite(F);
writeln(F, d);
closefile(F);
end;
That would be very bad if you would want to send such files to others, with different language settings.

Why would you like to write any numerical number as Text? Writing the number 1.001 as text would require 5 * 8 = 40 bits of data or 5 bytes of data. Writing the same number as Single would require 4 * 8 = 32 bits od data or 4 bytes of data.
Also writing this number in numerical form makes sure that number isn't afected by computer language settings.

User137
11-02-2013, 06:57 PM
Floating point values are written as text in many file formats, for example OBJ (http://en.wikipedia.org/wiki/Wavefront_.obj_file), INI, various XML formats and so on. Text files are easier to debug. I don't think there is anyone in this world who actually reads with thousand-separators, at least when it comes to programming. Something like MS Office may print such separators though, and even then it could internally be stored as number, not string.

SilverWarior
11-02-2013, 10:34 PM
I must admit that lots of programs stores numerical values in textual form but I can't understand why. Sure it is good to be able to see some of theese values outside the program sometimes but in most times you wouldn't want those values to be so easy accsesible since changing any of those can have huge impact on your program.
Also converting values from textual form and back is quite slow process. Textual representation of theese values usually takes more space to store. And finally the sligthest data corruption (one letter inserted in the value) and you get that nasty "String conversion fail" Exception which would deffinitly scare many customers away from using your programs. Nobody likes seeing Exception windows poping up in your applications.

So personally I avoid storing any numerical value in textual form.
I also try avoiding the use of XML files. Sure the original idea of XML is to make filetype which would alow sharing information stored in it between different applications and even systems. But reality is that XML is far from that. If you are monitoring other general programing forums you can see that quite some peepole are having problems of extracting the data from XML files they got somwhere around the web. Finally many of them solves their problem by hardcoding the data structure of XML file into their program despite the fact that every XML should already contain the necessary information to get the data structure out of it automaticly.

robert83
18-02-2013, 03:08 PM
Hello,

I'm stuck... I just dont know how to solve this :


procedure TMain.ScrollMap;
var x,y,z,i,j : integer;
begin
if RanOnce = false then
begin
for z:=0 to 3 do
SetLength(AnimTable.Layer[z].TileAnim, Map.MapLength);
if MapOffset in [0..15] then
begin
MapOffset:=Map.MapLength-15;
end;
LineHeight:=0;
GameDrawGrid;
RanOnce:=true;
end;

if MapOffset > 0 then
begin
i:=-1;
LineHeight:=LineHeight+ScrollSpeed;
if LineHeight = 32 then
begin
Dec(MapOffset,1);
OffsetEdit.Value:=MapOffset;
LineHeight:=0;
end;
// Background : Layers [0..3], Tiles [0..19], Lines [0..15] of TBackGround;
// Map.Record : Layers [0..3], Lines [0..Map.Length-1] , Tiles [0..19]
for y:=0 to 15 do
for x:=0 to 19 do
begin
// move existing sprites down
if BackGround[0,x,y] <> nil then
begin
if LineHeight = 32 then i:=y;
BackGround[0,x,y].Y:=BackGround[0,x,y].Y+ScrollSpeed;
if BackGround[0,x,y].Y = 480 then
begin
i:=y;
BackGround[0,x,y].Dead;
BackGround[0,x,y]:=nil;
end;
end;
end; // X loop
for j:=0 to 19 do
begin
if (i <> -1) and (Map.Layer[0].Lines[MapOffset-1,j].ImageIndex <> 0) then
begin
BackGround[0,j,i] := TBackGround.Create(SpriteEngine);
BackGround[0,j,i].ImageName:= Images.Item[1].Name;
BackGround[0,j,i].X:=j*32;
BackGround[0,j,i].Y:=-32;
BackGround[0,j,i].Z:=0;
end;
end;

end; // if Offset



What I've tried to do here is, move every AVAILABLE sprite in the BackGround sprite array down, and then kill it off, when one of them Lines is killed a new line should be created in it's place. The above "almost" works perfectly if there is a Sprite on the beggining of each line, but won't do a thing when there is none, I ...damn... I just can't figure it out. Also for some reason when it reaches the end of the map there is a difference in Sprites between the EditorMode and the GameMode, to be exact the difference is 7 . The code above is probably pretty sh*tty, I've tried to combine things, move code around...the thing is I don't understand this at all.

Original code which I think is the only part that is clean, and I understand what it does and it does that to :) is this :


if MapOffset > 0 then
begin
LineHeight:=LineHeight+ScrollSpeed;
if LineHeight = 32 then
begin
Dec(MapOffset,1);
OffsetEdit.Value:=MapOffset;
LineHeight:=0;
end;

for y:=0 to 15 do
for x:=0 to 19 do
begin
// move existing sprites down
if BackGround[0,x,y] <> nil then
begin
BackGround[0,x,y].Y:=BackGround[0,x,y].Y+ScrollSpeed;
if BackGround[0,x,y].Y = 480 then
begin
BackGround[0,x,y].Dead;
BackGround[0,x,y]:=nil;
end;
end;
end; // X loop
end;


I know what I must do , but I dont know how to tell the machine to do it.
1. Move every available sprite inside SpriteArray
2. When a sprite reaches the bottom it is killed
---- problem zone here ----- I'm not sure what to do....
since there are empty spaces, and EMPTY lines as well my logic is stuck here
3. I should create a new line of Sprites inside the array but : ( I'm seriously confused here )
-- do I need to take into account from which line of the SpriteArray did I previously destroy the Sprites , which reached the end of level . I think yes (the first code does that, altought not very clean)
-- I don't know how to check which line of the SpriteArray was destroyed if there are Lines with NO Sprites at all

I'm also attaching my code , with 2 maps , I'm so confused that you'll probably understand better if you load the two maps and see the problem .

Greetings
Robert


Attached is a PDF file with a drawing showing what is happening , or atleast I think it goes like that...

SilverWarior
19-02-2013, 01:59 AM
The way I have solved this in the Editor rewrite I'm doing for you is that I instead of checking by sprites position I'm actually checking by the map offset position.
Since you know that each line has certain position on the map (Line.YPos := LineIndex * LineHeight). But this approach requires you to threat your map a bit differently.

Easiest way to add this into your code would be so that before you create line of sprites you also create TObject list which would hold pointers to the sprites you would create for that specific line. So when it comes time to remove sprites from this certain line you just iterate through TObject list items and cal Dead procedure for each of theese sprites.


As for the my progress on Editor I had to rewrite most of my code so that now I have posibilty to include some aditional functionality I hadn't plan before.
Features that I currently plan to include are:
1. Multilayer support -offcourse
2. Two layer types:
- grid based layer type is verry similar to the one you use now. Tiles are positioned on the grid
- section based layer is a bit different. This layer alows positioning Sprites ony any position. You would probbably use this one for enemy units and such.
Each of the layers would have its own procedure for taking care of Sprite creation and destruction.
3. Tiles are now declared as classes to alow data sharing. For instance you create a water Tile data, and then simply reuse this data for creating WaterTiles on different parts of map Tiles can also have pointers to AnimationData class and Entity class.
4. AnimationData class would serve for controlling Sprites animations. It contains all the needed data for animations like number of frames animation has, animatiomn speed, animation start position. I also plan to improve this by adding ability to stop or start animation at any time. And this woulld be posible to do instantly or by speeding up or slowig down the animation (gradually stopping the windmill for instance). And since you can share dataq from AnimationData class between different tiles it serves as perfect way for tile animation synchronization.
5. Entity class would be used for things like buildings, units, etc. This would be mostly used in game as an easy way to figure ut which unit or structures health should be lowered when you do colision detection between different sprites.

Main reasony why I use classes rather than records are class properties. Why? Class property can actually automaticly return you a value which is calculation of several class internal properties or in our case to actually forward the values of conected classes. In layman terms this means that you would be able to acces all required values from connected AnimationData and Entity classes just as these values would be saved in Tile class. And if you Tile class is not conected to AnimatioData or Entity class properties would still return some predefined values.

Anywhay there is still quite some work to be done but I'm progressing gradually.

Now I would ask you if you could either found diferent shore graphics which doesn't contain any water are compleetly transparent on the place where water should be. This way I could test water and shore layering.
I would also be glad if you could provide me your textures in native format as files so I can also try some of my ideas (ability to place whole tree with one click for instance).

phibermon
19-02-2013, 11:08 AM
Main reasony why I use classes rather than records are class properties. Why? Class property can actually automaticly return you a value which is calculation of several class internal properties

I Use them a lot too, although I've started to favour public setter procedures over writable properties simply to make my classes easier to read, no need to check properties in a class to see what they do. I've found it's a great time-saver as it removes any element of ambiguity that arises from writable properties, which is good when you're working on a code-base so big that you can't possibly remember everything you did and why

robert83
20-02-2013, 10:11 AM
I did it :)


Scrolling works now, and I've tested it in multiple scenarios , and I understand why it works now , so simple :) (now...)

1. I move every available sprite down in the BackGround array
2. Once I reach 32 with my LineHeight counter (that means I went trough 1 line of Map data) , i do the following :
a) locate empty line inside BackGround array
b) read next line of MapData and put it inside of this empty line inside the BackGround array



procedure TMain.ScrollMap;
var x,y,z : integer;
i : integer;
a,b,c : integer;
begin
if RanOnce = false then
begin
for z:=0 to 3 do
SetLength(AnimTable.Layer[z].TileAnim, Map.MapLength);
if MapOffset in [0..15] then
begin
MapOffset:=Map.MapLength-15;
end;
LineHeight:=0;
GameDrawGrid;
RanOnce:=true;
end;

if MapOffset > 0 then
begin
LineHeight:=LineHeight+ScrollSpeed;
// Background : Layers [0..3], Tiles [0..19], Lines [0..15] of TBackGround;
// Map.Record : Layers [0..3], Lines [0..Map.Length-1] , Tiles [0..19]
// move available sprites down
for z:=0 to 3 do
for y:=0 to 15 do
for x:=0 to 19 do
begin
if BackGround[z,x,y] <> nil then
begin
BackGround[z,x,y].Y:=BackGround[z,x,y].Y+ScrollSpeed;
if BackGround[z,x,y].Y = 480 then
begin
BackGround[z,x,y].Dead;
BackGround[z,x,y]:=nil;
end;
end;
end; // X loop
// screen scrolled down one line
if LineHeight = 32 then
begin
Dec(MapOffset,1);
OffsetEdit.Value:=MapOffset;
LineHeight:=0;
// locate an empty line in the BackGround array
if MapOffset > 0 then
begin
a:=0;
y:=0;
c:=0;
repeat
for z:=0 to 3 do
for x:=0 to 19 do
if BackGround[z,x,y] <> nil then
a:=-1;
if a = -1 then
begin
inc(y);
a:=0;
end
else
begin
b:=y;
c:=-1;
end;
until c = -1;
// get next line of tiles and put into free line in background array
for z:=0 to 3 do
for i:=0 to 19 do
if Map.Layer[z].Lines[MapOffset-1,i].ImageIndex <> 0 then
begin
BackGround[z,i,b] := TBackGround.Create(SpriteEngine);
BackGround[z,i,b].ImageName:= Images.Item[map.Layer[z].Lines[MapOffset-1,i].ImageIndex].Name;
BackGround[z,i,b].X:=i*32;
BackGround[z,i,b].Y:=-32;
BackGround[z,i,b].Z:=z;
// animation properties
if BackGround[z,i,b].PatternCount = 1 then
begin
BackGround[z,i,b].DoAnimate:=false;
end
else
begin
BackGround[z,i,b].AnimStart:=0;
BackGround[z,i,b].AnimCount:=map.Layer[z].Lines[MapOffset-1,i].AnimCount;
BackGround[z,i,b].AnimSpeed:=map.Layer[z].Lines[MapOffset-1,i].AnimSpeed;
BackGround[z,i,b].AnimPos:=AnimTable.Layer[z].TileAnim[MapOffset-1,i];
BackGround[z,i,b].DoAnimate:=true;
BackGround[z,i,b].AnimLooped:=true;
end;
end;
end;
end;
end // if Offset
else
begin
// Destroy sprites
for x:=0 to SpriteEngine.Count-1 do
SpriteEngine.Items[x].Dead;
// Clear dynamic array
for z:=0 to 3 do
for y:=0 to 15 do
for x:= 0 to 19 do
BackGround[z,x,y]:=nil;
// Start again
RanOnce:=false;
end;

end;




SilverWarrior :

-I'll get the art in my next post, I'll reasemble the tree, cause I've cut it out into parts from a larger image
-I'll also give you the shore tiles which are transparent

If you can then please move this thread to myprojects as you've suggested and name it Space Shooter Game Editor . (think by the end it will be more then just a simple Level Editor)

Next in plan :
a) speed set by map position
b) add snow (perhaps rain, but rain makes me depressed so i'll probably wont do that),
when game reaches a part of map for example I have a special tile that triggers snow , till another special tile stops it, I planed to make it snow like this :
-- snow would start faling at line 0 and be destroyed at line 2, next instance of snow would start falling at line 1 and be destroyed at line 3, etc (2,4,3,5,4,6...)
hopefully with this I would achive a sense of depth, that the snow is actually falling down. This way I could do this without Collision detection, which would probably slow it down pretty much.





----
As for the Object things, there was a reason I've used arrays, I must confess that I'll need to go back to reading and practicing object oriented programing, while I do understand the basics how it works , I did not write code at all in an object oriented way (excluding the Delphi 7 graphichs interface part , but that is automatic, so it doesn't count)
I've started reading Mastering Delphi7 , that is why I updated my variable, procedure etc...names, as well as why I started commenting in a different way.
----

Greetings
Robert

SilverWarior
20-02-2013, 11:39 AM
SilverWarrior :

-I'll get the art in my next post, I'll reasemble the tree, cause I've cut it out into parts from a larger image
-I'll also give you the shore tiles which are transparent

About the tree do also leave the pices that you have now.
What I have in plan is only to show tree preview (replacing default selection sprite and snaping its position to grid) when you are placing it and then programaticly place tree pices into proper tiles.


If you can then please move this thread to myprojects as you've suggested and name it Space Shooter Game Editor . (think by the end it will be more then just a simple Level Editor)

Done already ;)


Next in plan :
a) speed set by map position
b) add snow (perhaps rain, but rain makes me depressed so i'll probably wont do that),
when game reaches a part of map for example I have a special tile that triggers snow , till another special tile stops it, I planed to make it snow like this :
-- snow would start faling at line 0 and be destroyed at line 2, next instance of snow would start falling at line 1 and be destroyed at line 3, etc (2,4,3,5,4,6...)
hopefully with this I would achive a sense of depth, that the snow is actually falling down. This way I could do this without Collision detection, which would probably slow it down pretty much.

Why not just use a good snow animation tiles which you then move when moving the map.


As for the Object things, there was a reason I've used arrays, I must confess that I'll need to go back to reading and practicing object oriented programing, while I do understand the basics how it works , I did not write code at all in an object oriented way (excluding the Delphi 7 graphichs interface part , but that is automatic, so it doesn't count)
I've started reading Mastering Delphi7 , that is why I updated my variable, procedure etc...names, as well as why I started commenting in a different way.

Since you are reading Mastering Delphi 7 do pay close atention to the chapter about making your own components as it covers some concepts that you will need for understanding my code.
But since I have used some more advanced concepts that aren't presented there (data sharing between classes) I'm writing my own tutorial about that.

Also if all goes wel I will ask Primož Gabrijelčič for opinion wheter my aproach is thread safe or not (I'm atending a Multithreading course tomorow) so pepole could easily use it in multithreading applications.

robert83
20-02-2013, 02:28 PM
Here it is , see attachment.

Yep about the snow, you are right, for that I'll need a Animated Snow tile , that is two tiles in Height, and I'll get the same depth effect and it will be much less expensive....


A few questions about this Editor / code :

1. I've tried it on multiple machines, and there is a strange thing (I'll try to explain) . The map scrolling even though it says 60FPS always , does not seem smooth... it feels like its going down at a variable speed (which is impossible since I'm moving it by 4 always....) Any ideas why?

2. Also on one machine Amd Sempron 7100GS , it hangs at random places , the computer itself freezes completely... need to reboot. But on my computer, and another one ( I have a few of them, for them LanParty's) it goes without a problem. In fact yesterday I left it to run all night to see if I have a memory leak, or some other bug somewhere. I guess the 7100GS is overheating and that is why it hangs.... anyway if you could take a moment to look at my Scrollmap
procedure to see if there is some potentialy dangerous code in there...that under certain circumstances might be problematic.

Anyway what anoys me the most is problem number 1 , maybe it's because I'm not fullscreen ? Maybe because I'm using Panel as renderingsurface? Is this because VSYNC ? I'm not sure if I've set MaxFPS correclty and SPEED, is there a magic ratio between the two that should be input ?


3. A side-side question :
I've got a Celeron 2000, GeForce 2GTS can only push out 30FPS max... , what is the culprit here ? My code ? Asphyre / SpriteEngine combo? I'm asking this because (and now I'm probably comparing apples to oranges) for example Tyrian 2000
runs smoothly on ever lower specs... am I limited here by the programing language itself ? ( or by my knowledge to write fast code ) - I'm asking this because I always think it's my falt, but not 100% sure, I just want to make this 100% clear, so that I don't expect it to run as fast on older hardware ...when it's simply not possible ... (cause it uses some modern DirectX stuff that is even though my sprites themselves are not complex , it still taxes older generation gpu's more... )
Greetings
Robert

SilverWarior
20-02-2013, 04:48 PM
1. I've tried it on multiple machines, and there is a strange thing (I'll try to explain) . The map scrolling even though it says 60FPS always , does not seem smooth... it feels like its going down at a variable speed (which is impossible since I'm moving it by 4 always....) Any ideas why?

Why would it be imposible? If for some reason part of your code takes longer than the desired time between screen refreshes it will create slowdowns which might be seen as variating movment speed.


2. Also on one machine Amd Sempron 7100GS , it hangs at random places , the computer itself freezes completely... need to reboot. But on my computer, and another one ( I have a few of them, for them LanParty's) it goes without a problem. In fact yesterday I left it to run all night to see if I have a memory leak, or some other bug somewhere. I guess the 7100GS is overheating and that is why it hangs.... anyway if you could take a moment to look at my Scrollmap
procedure to see if there is some potentialy dangerous code in there...that under certain circumstances might be problematic.

Can't be sure why this is happening without even seing the code (currently at work). But there is stil posibility that the reason for theese freezes is in the Asphyre graphic engine itself.
Have you tried running the demos which come with the graphic engine? If they work OK then there is either problem in your code or in code for SpriteEngine. Unfortunately tracking this kind of freezes which make your whole computer to freeze is quite hard.


3. A side-side question :
I've got a Celeron 2000, GeForce 2GTS can only push out 30FPS max... , what is the culprit here ? My code ? Asphyre / SpriteEngine combo? I'm asking this because (and now I'm probably comparing apples to oranges) for example Tyrian 2000
runs smoothly on ever lower specs... am I limited here by the programing language itself ? ( or by my knowledge to write fast code ) - I'm asking this because I always think it's my falt, but not 100% sure, I just want to make this 100% clear, so that I don't expect it to run as fast on older hardware ...when it's simply not possible ... (cause it uses some modern DirectX stuff that is even though my sprites themselves are not complex , it still taxes older generation gpu's more... )
Greetings
Robert

GeForce 2GTS is rather old graphic card so there is posibility that it doesn't support some feature Asphyre graphic engine usualy uses and this forces the while graphic engine to go into compatibility mode where all the graphical processing is actualy made by CPU (software rendering).
I'll check the Asphyre engine code to se if there is any way of telling this.


In the mean time I recomend you download yourself Process Explorer http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx
Process Explorer is neat litle program which alows you to monitor resource usage on your computer. It even alows you to monitor resource usage per program.
So by using this program you can quickly see if your program is using lots of CPU time which might result in slowdowns. And if you have Windows Vista or newer you can also monitor GPU usage and even Graphical memory usage. Unfortunately this feature isnt available on Windows XP

Anywhay I'll take a look at your code when I get home to see if there are any dangerous places.

robert83
21-02-2013, 12:25 PM
I've been playing around with the other demos, though they use differnt Apshyre version so it seems... but I've noticed that when they create the asphyre Timer they set FPS to some extreemely large value like 4000 in the SHOOTER demo, and 15.0 to speed. If I set that speed my editor scrolls by to fast. So I've tried to change around the timer, and noticed for example if I set it to MaxFPS 60, and Speed 1 , the chopiness is even more evident, wonder if it's because I can't figure out howto set them timers properly? (I've disabled vsync completely..)


I think I've located the possible offiending line, I've changed the code when a new line of sprites is created I create always the selection_image :


BackGround[z,i,b].ImageName:='selection.image';


and now I think it's smooth , but how to solve this?


BackGround[z,i,b].ImageName:= Images.Item[map.Layer[z].Lines[MapOffset-1,x].ImageIndex].Name;


I think the problem is cause by the lookup of ImageName ... , I've looked trough the properties, but ImageIndex is something else , if I use ImageIndex := I get an error.... it would probably be fast if I could somehow specify ImageIndex instead of ImageName.

UPDATE2 :

Now that I look at it, there might be another problem , my Layer cycle, I think I'm not reading data optimaly first reading layer then line then tile, I think I'll need to modify it so tha I read line, then layer then tile . I think I'm wasting a lot of cycles here. Layer data for Line1, should be next to each other...currently it's Layer0 : line1,2,3,4... then Layer1 : line1,2,3,4... , now when I think about it , when the map array grows I'm jumping here and there inside it to get just one line of data... ( I did not start coding this yet...but I really hope this is my problem, and will magicaly solve my spead issue...if I fix this...)



for z:=0 to 3 do
for y:=0 to 15 do
for x:=0 to 19 do
begin
//code
end;


While I think by moving the Layer cycle inside I will cut my cycles down.



for y:=0 to 15 do
for z:=0 to 3 do
for x:=0 to 15 do
begin
// code here
end;


or maybe



for y:=0 to 15 do
for x:=0 to 15 do
for z:=0 to 3 do
begin
//code
end


I'll change my code the way I save it, and the way I load it...into file.

Greetings
Robert

SilverWarior
21-02-2013, 06:55 PM
I've been playing around with the other demos, though they use differnt Apshyre version so it seems... but I've noticed that when they create the asphyre Timer they set FPS to some extreemely large value like 4000 in the SHOOTER demo, and 15.0 to speed. If I set that speed my editor scrolls by to fast. So I've tried to change around the timer, and noticed for example if I set it to MaxFPS 60, and Speed 1 , the chopiness is even more evident, wonder if it's because I can't figure out howto set them timers properly? (I've disabled vsync completely..)

If you take a look at the timer you wil see that it has two values:
1. MaxPFS: Is used to control how many redraws can there be done in one second. This directly controls how often is OnTimer event being fired. You use OnTimer event to process code needed for rendering.
2. Speed: Is used to control the speed of your game. This directly controls how often is OnProcess event being fired. You use this event to proces the code which ontrols map movment, units movment, some ingame simulations etc.

The main problem in your case is that you only use OnTimer event to process everything while this one should only be used to process graphics. So go and move code which controll map movment into OnProcess event and there should be no more variation in map movment speed.



but how to solve this?


BackGround[z,i,b].ImageName:= Images.Item[map.Layer[z].Lines[MapOffset-1,x].ImageIndex].Name;



After some checking I figred out that the problem is in the SpriteEngine itself as it is always searching for proper image from TAsphyreImages using ImageName. And since it is doing this for every redraw of every sprite it is probably afecting the overal performance a bit.

I'm trying to modify code from SpriteEngine so that it would be posible to set proper image for certain Sprite by any other means than just by providing ImageName. But the way how Sprites are implemented right now is making it dificult to achive this.



I think the problem is cause by the lookup of ImageName ... , I've looked trough the properties, but ImageIndex is something else , if I use ImageIndex := I get an error.... it would probably be fast if I could somehow specify ImageIndex instead of ImageName.

Yes ImageIndex is used for entirely different purpose. What ImageIndex does is that it adds number to the ImageName. So if you have several images named Grass1, Grass2, Grass3, and so on you can acces theese images by providing ImageName and then seting Image index. So ImageName 'Grass' and ImageIndex 5 would be the same as if you would set ImageName to 'Grass5'.
I suspect that this was some old way for doing animations.
And the error you get when changing ImageIndex is becouse SpriteEngine is trying to render image whose ImageName can't be found in TAsphyreImages. SpriteEngine has no safety mechanism for handling such situations when desired image isn't found.

So I'll try to change TSprite class in a way so you would be able to directly pass TAsphyreImage to it. This can be quickly got by using AsphyreImages[ImageIndex].
Also I'll try to add safety mechanism to prevent those nasty errors when desired image isn't found.
But as sad before I have to figure out a way of how to implement this while still retaining old functionality so that my changes won't break any programs using this old ackward way.

SilverWarior
21-02-2013, 07:18 PM
Now that I look at it, there might be another problem , my Layer cycle, I think I'm not reading data optimaly first reading layer then line then tile, I think I'll need to modify it so tha I read line, then layer then tile . I think I'm wasting a lot of cycles here. Layer data for Line1, should be next to each other...currently it's Layer0 : line1,2,3,4... then Layer1 : line1,2,3,4... , now when I think about it , when the map array grows I'm jumping here and there inside it to get just one line of data... ( I did not start coding this yet...but I really hope this is my problem, and will magicaly solve my spead issue...if I fix this...)

Changing the order of how for loops are nested won't save you any cycles as for loop sizes remains the same. It could only confuse you even more.
Also if you decide to implement layers with different sizes of their grids you definitly need to process one layer after another and not jum from one layer to other since in that case the lines between different layers won't be on same positions.

robert83
21-02-2013, 07:56 PM
I don't remember where I've read, but I recall that some map editor tutorial in some other language did it like this :

Process Lines
Process Layers
Process Tiles

I've changed my code a bit accordingly. The problem still manifests itself on my other computer altought it's now rarer... now I get a bit of a slowdown ever 1-2 sec... instead of the constant variation...
The system in question is a Core 2 Duo E3xxx , 9800GTX. My system runs it without hicups , its a bit better E7500...

Even tough if I'm not saving cycles, I'm atleast getting data in a better order now no?

I mean
Layer0-Line1
Layer1-Line1
Layer3-Line1
Layer0-Line2
Layer1-Line2
Layer2-Line2

Instead of

Layer0-Line1
Layer0-Line2
Layer1-Line1
Layer1-Line2
Layer2-Line1
Layer2-Line2

Right now it might not be the biggest performance hit, but later it might be no? I mean if the array is much larger.... it most jump here and there inside the array because the lines for the layers are not right next to each other. Or maybe there is no difference in speed ?

Greetings
Robert

SilverWarior
21-02-2013, 08:17 PM
UPDATE: I managed to modify TSprite class so it now provides the ability to provide it with pointer ti TAsphyreImage it will use for rendering itself. So now you can easily call:

BackGround[z,i,b].SpriteImage:= Images.Item[map.Layer[z].Lines[MapOffset-1,x].ImageIndex];
I have changed the TSprite class in a way so that if you assing some image to new SpriteImage property my changed code is used, but if you still just assign ImageName old code is used.
This means that all old programs will still work.

The only advantage of this old system is that it alows you to dynamicly load and unload seperate textures from AsphyreImages component becouse doing so can affect indexes of other textures. So accesing textures using indexes in such scenario is out of question.
Anywhay I don't think you would need dynamic texture loading and unloading by yourself. But if you would there is still existng mechanizem to support this present.

Here is updated AsphyreSprite.pas file:

1106

SilverWarior
21-02-2013, 08:53 PM
I don't remember where I've read, but I recall that some map editor tutorial in some other language did it like this :

Process Lines
Process Layers
Process Tiles

Maybe this was necessary becouse of the overal map structure they used.



Even tough if I'm not saving cycles, I'm atleast getting data in a better order now no?

This mostly depends on your original map structure and where do you nead certain data.



Right now it might not be the biggest performance hit, but later it might be no? I mean if the array is much larger.... it most jump here and there inside the array because the lines for the layers are not right next to each other. Or maybe there is no difference in speed ?

When reading from multidimensional array which is stored in memory there isn't much difference. Even if you have to jump all ower the memory this would be barely noticable.
But if you would have been reading this data from HDD jumping around would cause significant performance difference.

robert83
22-02-2013, 12:31 PM
Meanwhile I rewrote my code, to see what happens if I load entire map , which is only 7500 sprites not to much to handle on my lover end pc (e3xxx,9800GTX) . And I'm moving it down like this :


for y:=0 to 299 do
for z:=0 to 3 do
for x:=0 to 19 do
begin
if BackGround[z,x,y] <> nil then
begin
BackGround[z,x,y].Y:=BackGround[z,x,y].Y+ScrollSpeed;
{ if BackGround[z,x,y].Y = 480 then
begin
BackGround[z,x,y].Dead;
BackGround[z,x,y]:=nil;
end; }
end;
end; // X loop


The map if fixed length for this test.

And behold... the problem is still there... on the other computer it has hickups... on new lines...I'm starting to wonder if my problem is this!

if BackGround[z,x,y] <> nil .... this takes longer to compare then say if I used numbers instead , and would use the Map array to see if there is something to move or not....

if map.Layer[0].Lines[y+MapOffset,x].ImageIndex <> 0 then

UPDATE : rewrote the code that moves everything like this :


if MapOffset > 0 then
begin
LineHeight:=LineHeight+ScrollSpeed;
// Background : Layers [0..3], Tiles [0..19], Lines [0..15] of TBackGround;
// Map.Record : Layers [0..3], Lines [0..Map.Length-1] , Tiles [0..19]
// move available sprites down
for y:=0 to 299 do
for z:=0 to 3 do
for x:=0 to 19 do
begin
if Map.Layer[z].Lines[y,x].ImageIndex <> 0 then
begin
BackGround[z,x,y].Y:=BackGround[z,x,y].Y+ScrollSpeed;
end;
end; // X loop
// screen scrolled down one line
if LineHeight = 32 then
begin
Dec(MapOffset,1);
LineHeight:=0;
end;
end // if Offset
else
begin
// Start again
RanOnce:=false;
end;


Same effect... I'm right now in the process of making it full screen, load this map automagically in full screen start scrolling and see what happens...without any components on the form

Update 3:

I've removed everything from my code, it is fullscreen, just loads the map file and scrolls trough it... it's not creating sprites or anything, it just creates them once and then moves them down...and still it sometimes is choppy on the other computer

Attached the code and source, note if you do want to start it, make sure the map file is on c:\ , if you press ESC during the scroll it will go back to windows.

Greetings
Robert

User137
22-02-2013, 04:44 PM
BackGround[z,x,y].Y
Your graphics consist of continuous tiles right? So why does each tile have its own X, Y variable? They have their array position already, that tells exactly where they are at on game screen. IE x*TileWidth, y*TileHeight or something.

So code like this would be unnecessary:

if BackGround[z,x,y].Y = 480 then
begin
BackGround[z,x,y].Dead;
BackGround[z,x,y]:=nil;
end;

SilverWarior
22-02-2013, 09:47 PM
@robert83
Recheck my posts. In one of my previous posts I wrote about how to correctly use TAsphyreTimer.
The problem is that you are only using one of TAsphyreTimer events to do both rendering and map movment which means that overal CPU usage on some slow machine would greatly affect the smothnes of scrolling and great FPS variations. Also doing so prevents you to controll your MaxFPS and your game speed correctly.

robert83
24-02-2013, 11:16 AM
@SilverWarrior ,

I did it, and to my dissapointment, it's worse then it was before on the other computer :


procedure TMain.ATimer1Process(Sender: TObject);
begin
if StartDemo = true then
ScrollMap;
end;




procedure TMain.ATimer1Timer(Sender: TObject);
begin
// draw stuff , background is black
ADevice1.Render(clBlack, True);
// do stuff that require steady speed here
ATimer1.Process;
// flip backbuffers , make it visible
ADevice1.Flip();
end;


ATimer1.MaxFPS:=120;
ATimer1.Speed:=60;

Now even on my computer I get a hickup once... on the other computer constant hickups.

I've just tried to simply down my Scrollmap procedure to this :


procedure TMain.ScrollMap;
var x,y,z : integer;
i : integer;
a,b,c : integer;
begin
if RanOnce = false then
begin
if MapOffset in [0..15] then
begin
MapOffset:=Map.MapLength-15;
end;
LineHeight:=0;
GameDrawGrid;
RanOnce:=true;
end;

if MapOffset > 0 then
begin
LineHeight:=LineHeight+ScrollSpeed;
// Background : Layers [0..3], Tiles [0..19], Lines [0..15] of TBackGround;
// Map.Record : Layers [0..3], Lines [0..Map.Length-1] , Tiles [0..19]
// move available sprites down
for y:=0 to 15 do
for z:=0 to 3 do
for x:=0 to 19 do
begin
if BackGround[z,x,y] <> nil then
begin
BackGround[z,x,y].Y:=BackGround[z,x,y].Y+ScrollSpeed;
if BackGround[z,x,y].Y = 480 then
begin
BackGround[z,x,y].Y:=-32;
end;
end;
end; // X loop
end; // if Offset
end;



My OnRender has only this

procedure TMain.ADevice1Render(Sender: TObject);
begin
FPSEdit.Value:=ATimer1.FrameRate;
SpriteEdit.Value:=SpriteEngine.Count;
MemEdit.Text:=FormatFloat(',.# KB', CurrentMemoryUsage / 1024) ;

if StartDemo = true then
begin
// ScrollMap;
// AnimationPositionCalculator;
end
else
begin
if EditorAnimationCounter = 1000 then EditorAnimationCounter:=0;
inc(EditorAnimationCounter,1);
EditorXPos:=floor(myMouse.Position.X / (Panel1.Width / 20 ));
EditorYPos:=floor(myMouse.Position.Y / (Panel1.Height / 15 ));
with EditorCursor do
begin
X:=EditorXPos*32;
Y:=EditorYPos*32;
end;
end;
// SpriteEngine.Collision;
SpriteEngine.Dead;
SpriteEngine.Move(1);
SpriteEngine.Draw;
end;



ATimer1.MaxFPS:=1000;
ATimer1.Speed:=60;

And while the FPS is 1000 + on the other machine ( Windows 7 32bit, E3xxx, 3GB ram, 9800GTX+ ) , it does have hickups... and now I'm just scrolling existing sprites, I'm not updating, re-drawing, i'm just setting the position back to -32 . What am I possibly doing wrong here ? Is this not the way to go trough the sprites ? Is the Array causing the problem , the array I'm using to manipulate the sprites?

UPDATE : no-no-no-no :( :( :(
I've modified the code a bit further to see if the array can be the culprit :


for y:=0 to 15 do
for z:=0 to 3 do
for x:=0 to 19 do
begin
if Map.Layer[z].Lines[y+MapOffset-1,x].ImageIndex <> 0 then
begin
BackGround[z,x,y].Y:=BackGround[z,x,y].Y+ScrollSpeed;
if BackGround[z,x,y].Y = 480 then
begin

BackGround[z,x,y].Y:=-32;
end;
end;
end; // X loop


This array is much larger then BackGround array. 18000 vs 960 things to check with IF . Guess the E7500 is fast enough not to make it noticable to go trough 960 ... but the E3xxx (Celeron....) is not.
And now I have the same hickups on this faster computer, guess it takes to long to go trough the arra with if's like this, but how can I speed it up ? Can you give any tips ? ( and keeping it using array's , for now...)

UPDATE2:

I reran the code just to see it again, while I was googling for tips, changed nothing, and it runs smoothly on my machine again... with this


for y:=0 to 15 do
for z:=0 to 3 do
for x:=0 to 19 do
begin
if Map.Layer[z].Lines[y+MapOffset-1,x].ImageIndex <> 0 then
begin
BackGround[z,x,y].Y:=BackGround[z,x,y].Y+ScrollSpeed;
if BackGround[z,x,y].Y = 480 then
begin

BackGround[z,x,y].Y:=-32;
end;
end;
end; // X loop



I'm getting pretty confused here, now it runs fine. Am I still doing something wrong here ? Or the Timer itself is playing with me...

UPDATE 3 :
re-wrote parts of the code to use Timer1.Process() . I've sepereated the Movement from the Line Creation . Sometimes it goes smooth on my computer, sometimes it's choppy on this one as well, as for the other "slower" machine it's choppy always even though I have 120FPS constantly...
I've attached the code, I think I'm having some serious problem understanding something here. Can you please take a quick look at the Timer.Process(), Device.Render() , DemoScrollMapLines(), DemoDrawMapLines() ?
Attached full source code, without exe.

I've looked again at the Diablo like example that comes with sprite engine, but there is nothin in Timer.Process() , guess because it's done using OnMove for all Objects, and OnMove is probably tied to Timer1.Speed ?

ps.: sorry a bit confused here, probably wrote to much :)

Greetings
Robert

SilverWarior
25-02-2013, 07:20 AM
I did it, and to my dissapointment, it's worse then it was before on the other computer :


procedure TMain.ATimer1Timer(Sender: TObject);
begin
// draw stuff , background is black
ADevice1.Render(clBlack, True);
// do stuff that require steady speed here
ATimer1.Process;
// flip backbuffers , make it visible
ADevice1.Flip();
end;



You should always call ATime1.Process last. So code would look lie this.


procedure TMain.ATimer1Timer(Sender: TObject);
begin
// draw stuff , background is black
ADevice1.Render(clBlack, True);
// flip backbuffers , make it visible
ADevice1.Flip();
// do stuff that require steady speed here
ATimer1.Process;
end;



I also owe you a GREAT apology. Why? Becouse for the past few weeks I have been unintentionally misleading you to wrong direction.
When I first started to help you I asumed that main problem you have is the fact that Sprite Engine you use doesn't have optimization code which would render only visible sprites.
I must admit that my asumption was compleetly wrong (should have studied Sprite Engines code in the first place). Sprite Engine does have necessary code to prevent nonvisible sprites of not being rendered.
The reason why you had so poor performance in the start was becouse Form which you used for rendering target was so big that optimization code hasn't even kicked in as it asumed that all that should be rendered even when most of that Form wasn't even visible on the screen. It was as if you would be rendering to realy large screen.

What does this means?
You CAN create sprites for the whole map.
Instead of moving each sprite you SHOULD only cahnge world position.

So you see most of my suggestions to you were wrong. I haven't realized this until yesteray when I studied Sprite Engines code compleetly.
I hope you can forgive me for this.

robert83
25-02-2013, 10:09 PM
No problem , it was fun coding that stuff :) , and I still think it might come handy later. And after testing it out the way you told me to, I think I'll need it still...

I've looked into the AsphyreSprite code as well, you mean this part takes care of what to draw and what not to draw?


procedure TSprite.Draw;
var
ImgName: string;
begin
if (FX > FEngine.WorldX + FVisibleArea.Left) and
(FY > FEngine.WorldY + FVisibleArea.Top) and
(FX < FEngine.WorldX + FVisibleArea.Right) and
(FY < FEngine.WorldY + FVisibleArea.Bottom)then
begin
if not FVisible then Exit;
case ImageType of
itSingleImage: ImgName := FImageName + IntToStr(FImageIndex);
itSpriteSheet: ImgName := FImageName;
end;


Anyway I created a few test maps to see if it does kick in , a map that is 1500 lines long, and has all layers filled with sprites , that is 120.000 sprites , and my framerate dropped to 75, then i've created a map that is 5000 Lines long
and has a total of 400.000 Sprites , and I get 26 FPS , I know these are scenarios which I will probably not reach on the long run. Even if the VisibleArea does work , cause I think it does I did another Editor to test it :


procedure TMain.EditorDrawGrid;
var x,y,z : integer;
begin
// 15 lines
for y := 0 to Map.MapLength-1 do
// 4 layers
for z := 0 to 3 do
// 20 columns
for x := 0 to 19 do
begin
// create/modify sprite with image
if map.Layer[z].Lines[y,x].ImageIndex <> 0 then
begin
// if sprite was not created I create it
if BackGround[z,x,y] = nil then
begin
BackGround[z,x,y] := TBackGround.Create(SpriteEngine);
BackGround[z,x,y].ImageName:= Images.Item[map.Layer[z].Lines[y,x].ImageIndex].Name;
BackGround[z,x,y].X:=x*32;
BackGround[z,x,y].Y:=y*32;
BackGround[z,x,y].VisibleArea:= rect(-32,-32,640,480);
BackGround[z,x,y].Z:=z;
end
// otherwise I only set new image
else
begin
BackGround[z,x,y].ImageName:= Images.Item[map.Layer[z].Lines[y,x].ImageIndex].Name;
end;
// animation properties
if BackGround[z,x,y].PatternCount = 1 then
begin
BackGround[z,x,y].DoAnimate:=false;
end
else
begin
BackGround[z,x,y].AnimStart:=0;
BackGround[z,x,y].AnimCount:=map.Layer[z].Lines[y,x].AnimCount;
BackGround[z,x,y].AnimSpeed:=map.Layer[z].Lines[y,x].AnimSpeed;
BackGround[z,x,y].AnimPos:=(EditorAnimationCounter mod BackGround[z,x,y].AnimCount)+map.Layer[z].Lines[y,x].AnimStart;
BackGround[z,x,y].DoAnimate:=true;
BackGround[z,x,y].AnimLooped:=true;
end;
end
// if sprite is empty, I kill it , and set the array entry to nil
else
begin
if BackGround[z,x,y] <> nil then
begin
BackGround[z,x,y].Dead;
BackGround[z,x,y]:=Nil;
end;
end;
end;
end;


BackGround[z,x,y].VisibleArea:= rect(-32,-32,640,480); if I make this smaller , like 380, the Visible area is smaller, but the speed stays the same not to mention the memory usage, it seems even though it's NOT drawing, the part that does the calculations and who knows what are still taking a considerable tax on the machine.


Anyway I've created a new version, using this simplified method, where I create all them tiles I need for the Entire Map, then I do the scrolling by moving to the WorldPosition:=(32*Map.MapLength)-480; (last screen) .
Then Using SpriteEngine.WorldY:=SpriteEngine.WorldY-ScrollSpeed (or be it 4) , I move the map .... and, I don't know I just dont know, it still has hickups on my computer now, the strangest thing is, sometimes when i start it it goes pretty good, then all of a sudden a hickup then goes good for a while... sometimes I start it and it's downright choppy (even tough I have a modest 300FPS now...using all sprites) .

I'm attaching the source code and a few images to show you what I mean. Can you take a look at the code, what am I doing wrong ?
Also I've commented out all the inttostr conversions and formatfloat during the rendering onprocessing to make sure that the problem is not cased by them...

UPDATE : the button DON'T PUSH ME , can be used to fill a map to the max with sprites.... (just carefull if map is very long, it takes a lot of time to create all them sprites...) 1500-5000 enough... for maplength

UPDATE2 : found this via google.com http://dev.ixchels.net/forum/archive/index.php/t-139.html , they talk about jerky movement, and it's explained why it's jerky in non fullscreen mode,...guess I'll try to redo in full screen the scroll only, maybe I've missed something....

UPDATE3 : did a quick test going fullscreen, removed all code again, only left the one necessary for loading level and moving, it still feels jerky if timer speed is 60 (max fps I get on laptop is around 100fps) , now If I move the movement code into device render is a lot smoother, but get that rollercoaster speed effect) , if I increase the speed to 100 on timer , then the movement is pretty fast and seems constant, though I'm not 100% sure it still files like it's doing the hickups but at a much quicker and constant rate. (or I'm seeing things, need someone to check this demo out) Attached is a fullscreen.zip , with executable, it goes fullscreen, map needs to be placed on c drive map is included inside zip, with ESC you can quit anytime, it just loops trough this one map... (I'm not sure if I'm doing this right, I'm right now moving the world down by 4 pixels ... I don't know why I keep thinking this but I think I should move the world map down by 1 pixel only at a much higher interval for worldmap movement only, is this not how it's done properly ?)

Greetings
Robert

robert83
26-02-2013, 07:59 AM
I could not attach to previous post for some reason , so here it is .

I've edited a bit more , and added BackGround.OnMove


procedure TBackGround.Move(const MoveCount: Single);
begin
inherited;
if StartDemo = true then
begin
WorldY:=WorldY+4;
end;
end;


Using this it scrolls trough one screen of my map, but wont create new lines, wonder why? I tried playing with VisibleArea on a per sprite bases when creating the map but could not get it working properly


BackGround[z,x,y].VisibleArea := rect(-32,(y*32-32),640,(y*32)+480));

The idea is that I'd create each sprite with a different Visible area... but for some reason, the above code does not do what I expected it to do...

so for now I'm doing onmove using

Y:=Y+4;

seems smoth now on my laptop in smoothscreen, maybe I must do movement in onMove ?

Greetings
Robert

robert83
26-02-2013, 09:30 AM
(Sorry for some reason I cannot get the file upload to show when editing my post)

Anyway I think I did it...



procedure TBackGround.Move(const MoveCount: Single);
begin
inherited;
if StartDemo = true then
begin
if SpriteEngine.WorldY = 0 then
SpriteEngine.WorldY:=(Map.MapLength*32)-480;
SpriteEngine.WorldY:=SpriteEngine.WorldY-0.0010;
end;
end;


Note I had to move SpriteEngine from Private to Public...

The strangest thing is :) , if it's running non fullscreen I get 27FPS probably the visible area not kicking in, but if I run fullscreen it runs at full 60 fps smooth , on this laptop with integrated gpu...I think that is a big word... I set the visible area for each sprite, I did set it on purpose to be less then screen size to see what happens and if it works.

So this means I have to make the Editor itself fullscreen to ? (or leave editor as is, since it's easier to work with components available, and do the game in fullscreen later....)

Update : if Form size is same as asphyre device size, it works in non full screen mode as well at same speed.
Question : why do I have to use such a low value to move the WorldY now ? 0.0010 , the Speed for Timer is 15 only...

Greetings
Robert

SilverWarior
26-02-2013, 10:11 AM
For the past two hours I was trying to track this problem but unfortunately I had no luck in going this. I even tried mesuring execution time for Rendering procedure where time needed for completing it was jumping between a few miliseconds to 15 miliseconds. But in worse case it jumped to 247 miliseconds and this happened when I wasn't even moving the map. Why did this happend I have no real answer. I can only suspect that some program has been doing something on the same core that it was asigned to the editor.
I even tried changing process afinity so that it alwasy ran on one of the cores and increasing the overal prioity but there were still theese hickups.

Gues I'll need to go and try profiling execution time for each even so small method, probably even those in Asphyre graphic engine itself to pinpoint the problem. I will notify you of my results when I have some.
But for now based on what is written on the link you provided theese hickups should be less comon when you run your application in FullScreen mode. But this means no VCL components so it might be only usable for game and not editor.

SilverWarior
26-02-2013, 10:32 AM
The strangest thing is :) , if it's running non fullscreen I get 27FPS probably the visible area not kicking in, but if I run fullscreen it runs at full 60 fps smooth , on this laptop with integrated gpu...I think that is a big word... I set the visible area for each sprite, I did set it on purpose to be less then screen size to see what happens and if it works.

Not so strange. Most games run better in FullScreen.


So this means I have to make the Editor itself fullscreen to ? (or leave editor as is, since it's easier to work with components available, and do the game in fullscreen later....)

I suggest that you leave editor as it is since you actually don't realy need smoth movment in editor. But when you would be designing your game you would deffinitly do it in FullScreen mode.
But if you are prepared to do lot of work you could made Editor to work in FullScreen mode but for this you would need to recreate all VCL components in it and this is nowhere an easy job. I know since I'm trying to do something like this myself. I'm developing a library which would alow me to create VLC-like components which would be rendered by AsphyreSphinx graphic engine. And since 2D portion of AsphyreSphinx graphics engine isn't so different than Asphyre Extreme engine I could probably port my library to it also.


Question : why do I have to use such a low value to move the WorldY now ? 0.0010 , the Speed for Timer is 15 only...

Speed of 15 in timer means that OnProcess will try to execute every 15 miliseconds which means aprox 66 times per second. You need to know that values in AsphyreTimer are just desired delays in miliseconds. And if Rendering takes to much time OnProcess would be executed more than once to catch up.