Yes, the same basic principles apply but you would need to modify the code to use 3d objects over 2d images.
Now On to the next part:
There is an attachment with all the current files and media, you may want to run that first before going on.
Objects
SImilar to the NPC's we are going to script all of our objects. This will take place with 2 files, one that defines them and one that creates them. Why you ask, well we may want to place 200 of the same item in the game and the last thing we want to do is have to define all the stats for those 200 items, better to instead define it once and then create 200 copies of it.
Lets take a look at the Object data (this is where we will define our objects).
<start_objects>
<new_object>
name:Bush
takeable:0
image:809
used_image:808
give:berries
<end_object>
<new_object>
name:Gold Key
takeable:1
image:804
zpos:0
used_image:804
inventory_image:901
<end_object>
<new_object>
name:Berries
Takeable:1
image:810
zpos:0
used_image:810
Inventory_image:900
<end_object>
<new_object>
name:Wooden Door
takeable:0
image:802
Used_image:803
Locked:1
open:gold key
<end_object>
<end_all>
so what do these mean...
<new_object> <-- this tells our parser we are about to describe a new item
name:Bush <-- this gives the item a name
takeable:0 <-- this lets us know if the item can be added to our inventory 0 means no and 1 (or more) means yes
image:809 <-- this gives the object its starting image.
used_image:808 <-- this tells the parser if the image changes after it has been used, in this case the base image is a bush with berries on it and the used image is a bare bush for after those berries are picked.
give:berries <-- this tells the parser what to give to the player if the item is used. This will not be defined for all items because some may not give something else, but in this case using the bush gives the player a pile of berries.
<end_object> <--This tells the parser we are done defining this object
A few other commands you may notice on the other items.
zpos:0 <-- this defines if the player can walk "behind" the item most items, like the bush, you can, but they key we are defining with this is tiny and should rest on the ground, our player should not walk behind it. so a zpos of 0 means the player is always drawn "over" the item.
Locked:1 <-- This tells the parser that this object blocks the players progress in some way, in this case a door. but it could also be applied to a chest, cabinet or anything else you want locked.
open:gold key <-- This defines the name of the object required to unlock this object, so in the case of our wooden door it is unlocked by a gold key.
as we get farther into the tutorial we will add some more fields such as a description for the inventory screen, value, attack damage etc. this is the part of the script where you will most likely create alot of your own commands to fit your game.
Now lets look at the Object Key
<start_objects>
<start>
Name:bush
xpos:5
ypos:7
<end>
<start>
name:bush
xpos:4
ypos:7
<end>
<start>
name:gold key
xpos:10
ypos:10
<end>
<start>
name:gold key
xpos:10
ypos:17
<end>
This is alot more straight forward. Here we create the instances of our objects, as you can see I have made two doors, bushes, and keys and by doing it this way we only needed to define them once. Each entry has a Start, the name of an object we created in our object data file, its map xposition and its yposition followed by the <end> command. This is the sort of thing you will eventually want to build an editor for to speed up placing you objects on the map.
Our new UDT's
type thing
xpos as integer
ypos as integer
takeable as integer
name as string
used as integer
image as integer
used_image as integer
inventory_image as integer
sprite_number as integer
zpos as integer
give as string
locked as integer
open as string
endtype
type possessions
name as string
image as integer
value as integer
endtype
The first one is to hold our objects it should be very easy to understand as it is just holding the data we just discussed.
the possessions type will be used for our characters inventory. it holds the items name, an inventory image, and the items value (which we are not using yet) we wont need to much more than that in this type because we will use it to reference the base objects as defined in our object data file.
New Global Variables:
global old_x as integer
global old_y as integer
these two variables will be used to hold our players position before an attempted move so we can bounce him off locked objects.
New Arrays
dim objects(-1) as thing <-- this stores the data for our base objects.
dim game_objects(-1) as thing <-- this stores the data for our instanced objects.
dim inventory(-1) as possessions <-- and this defines what is in our players inventory.
Initialization:
initialize_objects("objectdata.txt") <-- this function loads our data into our base objects
make_objects("objectkey.txt") <-- and this one creates our instances.
Additions to the main loop.
draw_objects()<-- this draws our objects to the screen.
sprite_depth()<-- this sets the imagined z position of the objects based on their screen y position to give the illusion of depth.
if array count(inventory())>-1
for a = 0 to array count(inventory())
text 0,a*text height("test"),inventory(a).name
next a
endif
Right now we have not yet addedd in an interface for our players inventory so this is printing the items our player is carrying to the left side of the screen. Later we will change this to a full inventory system.
obcol=object_collision()
if obcol>-1 and mouseclick()=1 then work_objects(obcol)
This is almost identicle to the checks we make for our NPC's we first check to see if the player is colliding with an object, and next we check to see if the player is attempting to use an object he is colliding with.
Additions to the Graphics sub file
function free_sprite(x)
repeat
inc x
until sprite exist(x)=0
endfunction x
function sprite_depth()
if sprite exist(1)=1 then set sprite priority 1,sprite y(1)
for a=10 to 100
if sprite exist(a)=1
set sprite priority a,sprite y(a)
endif
next a
for a = 0 to array count(game_objects())
if game_objects(a).zpos>0 then set sprite priority game_objects(a).sprite_number,sprite y(game_objects(a).sprite_number)
next a
endfunction
The first of these two functions,
Free_sprite(), is checking for the next unused sprite starting from whatever parameter we give it. This will be essential to bjects as we will be creating and deleting them constantly.
The second function is cycling through the player sprite and all object and NPC sprites and assigning them priority according to their y position. This will make it so items lower on the screen are drawn over items higher on the screen to add an imagined z dimension to our game.
Our new Object Functions:
function initialize_objects(file$)
open to read 1,file$
repeat
read string 1,x$
if x$="<new_object>"
objnum=construct_base_object()
repeat
read string 1,x$
if lower$(left$(x$,5))="name:"
objects(objnum).name=right$(x$,len(x$)-5)
endif
if lower$(left$(x$,5))="give:"
objects(objnum).give=right$(x$,len(x$)-5)
endif
if lower$(left$(x$,5))="open:"
objects(objnum).open=right$(x$,len(x$)-5)
endif
if lower$(left$(x$,6))="image:"
objects(objnum).image=val(right$(x$,len(x$)-6))
endif
if lower$(left$(x$,5))="zpos:"
objects(objnum).zpos=val(right$(x$,len(x$)-5))
endif
if lower$(left$(x$,9))="takeable:"
objects(objnum).takeable=val(right$(x$,len(x$)-9))
endif
if lower$(left$(x$,11))="used_image:"
objects(objnum).used_image=val(right$(x$,len(x$)-11))
endif
if lower$(left$(x$,16))="inventory_image:"
objects(objnum).inventory_image=val(right$(x$,len(x$)-16))
endif
if lower$(left$(x$,7))="locked:"
objects(objnum).locked=val(right$(x$,len(x$)-7))
endif
until x$="<end_object>"
endif
until x$="<end_all>"
close file 1
endfunction
function make_objects(file$)
open to read 1,file$
repeat
read string 1,x$
if x$="<start>"
objnum=construct_object()
base_object=-1
repeat
read string 1,x$
if lower$(left$(x$,5))="name:"
temp$=right$(x$,len(x$)-5)
for a=0 to array count(objects())
if lower$(temp$)=lower$(objects(a).name)
`base_object=a
game_objects(objnum).name=objects(a).name
game_objects(objnum).image=objects(a).image
game_objects(objnum).used_image=objects(a).used_image
game_objects(objnum).inventory_image=objects(a).inventory_image
game_objects(objnum).takeable=objects(a).takeable
game_objects(objnum).zpos=objects(a).zpos
game_objects(objnum).give=objects(a).give
game_objects(objnum).locked=objects(a).locked
game_objects(objnum).open=objects(a).open
endif
next a
endif
if lower$(left$(x$,5))="xpos:"
game_objects(objnum).xpos=val(right$(x$,len(x$)-5))
endif
if lower$(left$(x$,5))="ypos:"
game_objects(objnum).ypos=val(right$(x$,len(x$)-5))
endif
until x$="<end>"
game_objects(objnum).sprite_number=free_sprite(500)
sprite game_objects(objnum).sprite_number,-100,-100,game_objects(objnum).image
endif
until x$="<end_all>"
close file 1
endfunction
function construct_base_object()
array insert at bottom objects()
count=array count(objects())
objects(count).image=800
objects(count).name="Trash"
objects(count).takeable=0
objects(count).zpos=1
endfunction count
function construct_object()
array insert at bottom game_objects()
count=array count(game_objects())
game_objects(count).image=800
game_objects(count).name="Trash"
game_objects(count).takeable=0
endfunction count
function draw_objects()
if array count(game_objects())>=0
for a = 0 to array count(game_objects())
sprite game_objects(a).sprite_number,(game_objects(a).xpos-xpos)*16+xscroll,(game_objects(a).ypos-ypos)*24+yscroll,game_objects(a).image
next a
endif
endfunction
function object_collision()
count=-1
for a = 0 to array count(game_objects())
if sprite collision(1,game_objects(a).sprite_number)=1
count=a
if game_objects(a).locked>0
if array count(inventory())>-1
for incount=0 to array count(inventory())
if lower$(inventory(incount).name)=lower$(game_objects(a).open)
game_objects(a).locked=0
game_objects(a).image=game_objects(a).used_image
array delete element inventory(),incount
exit
endif
next incount
endif
endif
if game_objects(a).locked>0 then player(0).xpos=old_x:player(0).ypos=old_y
endif
next a
endfunction count
function work_objects(obcol)
if game_objects(obcol).takeable=1
add_to_inventory(obcol)
delete sprite game_objects(obcol).sprite_number
array delete element game_objects(),obcol
exitfunction
endif
if game_objects(obcol).image<>game_objects(obcol).used_image and game_objects(obcol).used=0 and game_objects(obcol).locked=0
game_objects(obcol).used=1:game_objects(obcol).image=game_objects(obcol).used_image
if game_objects(obcol).give<>""
if array count(inventory())<20
add_to_inventory_by_name(game_objects(obcol).give)
endif
endif
exitfunction
endif
endfunction
function add_to_inventory(obnum)
if array count(inventory())<20
array insert at bottom inventory()
inum=array count(inventory())
inventory(inum).name=game_objects(obnum).name
inventory(inum).image=game_objects(obnum).inventory_image
else
exitfunction
endif
endfunction
function add_to_inventory_by_name(name$)
for a = 0 to array count(objects())
if lower$(objects(a).name)=lower$(name$)
array insert at bottom inventory()
inum=array count(inventory())
inventory(inum).name=objects(a).name
inventory(inum).image=objects(a).inventory_image
endif
next a
endfunction
lets discuss these one by one. most of them are very similar to our NPC functions, especially our parsers.
Initialize_objects()
Just like our initialize NPC function this is taking our data dile and breaking the text down into its parts and assigning them to the variables in our Objects() array.
Make_objects()
Another Parser, but instead of reading in all the objects data it reads only the name and where to position it, it then cycles through all the base objects we made with our Initialize_objects() function and copies all the objects pertinant data from there.
Construct_base_object() and Construct_object()
JUst like our NPC's I made a base constructor, this will add an item to the appropriate array and assign it some base values. and by assigning these it will help with debugging our data since an object not properly defined will still be created but should be obviously wrong.
draw_objects()
If any objects exist this function makes sure to draw them all to the screen.
object_collision()
A little more to this function. it starts by checking to see if the player is colliding with any of the game_objects. I set count to -1 since it is outside the range of our array and will be used as a check later. If the player is colliding with an object it sets our count to the current object.
count=-1
for a = 0 to array count(game_objects())
if sprite collision(1,game_objects(a).sprite_number)=1
count=a
Now if a collision did happen we first want to see if the object is a "locked" object. If it is we check idf there are any items in the players inventory. and if there are we cycle through them to see if any of them are an item that can "unlock" the object we are colliding with. If it is we set the object to unlocked (locked=0), and set the objects image from starting (image) to used (Used_image). we then delete the object used to unlock the object we are colliding with, and exit the loop (if we don't it will delete every copy of that item in inventory). You may however want to add some code where an item can "unlock" many things and will not be deleted.
if game_objects(a).locked>0
if array count(inventory())>-1
for incount=0 to array count(inventory())
if lower$(inventory(incount).name)=lower$(game_objects(a).open)
game_objects(a).locked=0
game_objects(a).image=game_objects(a).used_image
array delete element inventory(),incount
exit
endif
next incount
endif
After all this we check again to see if the item is locked (if it is the player obviously did not have an item to unlock it). if it is we reset the players x-y position to his position before the collision thus keeping him from passing.
if game_objects(a).locked>0 then player(0).xpos=old_x:player(0).ypos=old_y
finally we return the number of any objects the player is colliding with.
Next we have the function Work_objects(). It is called only idf the player is colliding with an object and is pressing the left mouse button.
if game_objects(obcol).takeable=1
add_to_inventory(obcol)
delete sprite game_objects(obcol).sprite_number
array delete element game_objects(),obcol
exitfunction
endif
what this part of the code does is see if the item can be taken by the player, if it can it adds it to inventory, deletes the objects sprite to free it up and then deletes the object instance from the game. then following that it exits the function.
if game_objects(obcol).image<>game_objects(obcol).used_image and game_objects(obcol).used=0 and game_objects(obcol).locked=0
game_objects(obcol).used=1:game_objects(obcol).image=game_objects(obcol).used_image
if game_objects(obcol).give<>""
if array count(inventory())<20
add_to_inventory_by_name(game_objects(obcol).give)
endif
endif
exitfunction
endif
Now if we get to this part of the function then it is obvious that the object cannot be taken, so now we will check if the object can be used, and if it can does it give something to the player. if the objects base image and used image are not the same then it has not already been used, we make sure the item is not locked then update the image to the used image and give anything to the player that they are supposed to get.
The next function Add_to_inventory() is used when a player picks up an object that is takeable. we passed the game_objects number into the function. it will copy the appropriate info into the inventory array after making sure the palyer can fit it into their inventory.
function add_to_inventory(obnum)
if array count(inventory())<20
array insert at bottom inventory()
inum=array count(inventory())
inventory(inum).name=game_objects(obnum).name
inventory(inum).image=game_objects(obnum).inventory_image
else
exitfunction
endif
endfunction
and our last function.
function add_to_inventory_by_name(name$)
for a = 0 to array count(objects())
if lower$(objects(a).name)=lower$(name$)
array insert at bottom inventory()
inum=array count(inventory())
inventory(inum).name=objects(a).name
inventory(inum).image=objects(a).inventory_image
endif
next a
endfunction
Sometimes the player is given an object that has not been instanced yet, as in the berries from the bush, or a quest item from an NPC. so in this case rather than have it copy the information from a GAme_object it instead looks up the objects name and clones the information from a Base_object. This will be useful for alot of things, like random treasure from a chest, quest rewards, items bought from a shop etc.
And that is it for this part of the tutorial. You now have a near complete engine (though not refined) and could make alot of games with the info you have already. Now it is time to really start to put all of this together and start creating the data, maps, etc you want for your game. Next up Monsters.
I have noticed very little activity in this thread and really I would like to hear your thoughts (if anyone is reading this) your questions, your gripes, just something
Especially like to hear opinions from those who have been around here a long time or those who have writtten tutorials before for some advice on how to perhaps make this better.