3D games with Monkey + MiniB3D
Monkey Archive Forums/Monkey Tutorials/3D games with Monkey + MiniB3D
| ||
This thread will become a continuing tutorial about the Monkey Module MiniB3D. Every lesson will be become one post, more chapters will follow, The tutorial is for beginners and for users, who are already used to work with the old Blitz3D. It will show the differences in coding with Monkey. All samples will run on HTML5 This chapters are ready: - Chapter 1: Download the Module - Chapter 2: Test a first sample code - Chapter 3: More Entities - Chapter 4: Parents and Children - Chapter 5: Easy Collisions - Chapter 6: Textures - Chapter 7: Sprites and here is .... Chapter 1: Download the Module For the current version of MiniB3D you also need the current version of Monkey. So at first update your Monkey. The MiniB3D module is now avaiable as a ZIP file. Download it from here... https://github.com/adamredwoods/minib3d-monkey/archive/master.zip ...and extract it to your Monkey Module folder... C:\MonkeyPro69\modules\ After this the minib3d folder will have the wrong name: C:\MonkeyPro69\modules\minib3d-monkey-master\ Rename it to "minib3d"... C:\MonkeyPro69\modules\minib3d\ That's all! |
| ||
Chapter 2: Test a first sample code: As a first test, the following sample code should run without problems: (Screenshot) You will see a turning cube in a blue empty world. This sample code can be a template for your future minib3d projects. It does not contains the minimal possible code template for midib3d, but it contains all elements you need for watching something on the screen: A world, a camera, a light and a object. On the top it starts with the directive Strict which is alway recommended for preventing typos in your code. Then it imports two essential modules "mojo" and "minib3d". As all monkey projects you need to define a Game() class and a Main() function, which calls your Game() class. The three essential 3D elements TCamera, TLight and TMesh are defined as Fields in the Game Class. They will be invoked in a Init() Method later. Normally all game elements are invoked in the OnCreate() method, but with Minib3D you can do this only when the app call for the first time the OnRender() method. So, you see in OnCreate() nearly nothing happens. The command SetRender() creates the 3D world. Also OnUpdate() is returned immediately until the Init() is not yet started. Now, the first OnRender() call is important for MiniB3D. In this moment you call your method Init() to set and invoke your 3D elements. After this you can set Camera, Light and a first Object. A variable Started% prevents not to call this Init() again. After this first call of Init() in OnRender() you now can work with your 3D world. The command RenderWorld() always renders the current state of all 3D elements to the screen and is indispensable. Also the call of UpdateWorld() in OnUpdate() is essential. ...to be continued.... |
| ||
Chapter 3: More Entities A 3D object in MiniB3D is called "Entity". You can create them new by using primitives or by copying existing entities. This primitives are avaiable: CreateGrid(xSsegments, ySegments, Repeat, Parent) ' a plane CreateCube(Parent) ' a cube or a block CreateSphere(Segments,Parent) ' a bowl or ellipsoid CreateCylinder(Segments, Solid, Parent) ' a cylinder or tube CreateCone(Segments, Solid, Parent) ' a cone CreateLine(Vector1, Vector2, Thickness, Parent) ' a line As there is no documentation of MiniB3D at the moment you can have a look on the sourcecode files to inform you about all features and functions. Click on the tab "Projects" in monkey's browser on the right side: There open "modules - minib3d -tmesh.monkey". Now change to the browser tab "Code" and open the tree "TMesh". With a click on a command you will see the corresponding source code on the left and in the first code line with the function call you can inform about parameters: for example: "modules - minib3d - tmesh.monkey - CreateSphere" ' Function by Coyote Function CreateSphere:TMesh(segments:Int=8,parent_ent:TEntity=Null) If segments<2 Or segments>100 Then Return Null .... This means, that the CreateSphere() command has two parameters. The first one is the resolution quality and a second gives you the possibility to add this Entity to a parent entity. The "Entities" can be manipulated in many ways. A look into the source code of "modules - minib3d - tentity.monkey" shows us all methods. Here are the most important: .CopyEntity() ' creates a new entity from a existing one .X() ' return the coordinates of the entity .Y() " .Z() " .PositionEntity(X,Y,Z) ' puts the entity on a new coordinate .MoveEntity(addX,addY,addZ) ' moves the entity .ScaleEntity(X,Y,Z) 'sizes the entity .RotateEntity(X,Y,Z) ' puts the entit i a new orientation .TurnEntity(addX,addY,addZ) ' turns the entity .EntityColor(R,G,B) .ShowEntity() ' .HideEntity() .... In the following sample code you can see some of them in action. It's a world of buildings made by CopyEntity(). The TurnEntity() and MoveEntity() will drive the camera like a car . Use the MouseX() to control direction. |
| ||
Nice! :) |
| ||
Chapter 4: Parents and Children If you already worked with the old Blitz3D or MiniB3D on BlitzMax you will recognize a lot of the commands and syntaxes in the new Monkeys MiniB3D. But if you are completely new to 3D programming you will miss the documentation. As a makeshift you may find helpful the vintage manual of Blitz3D at... sorted by category: http://www.blitzmax.com/b3ddocs/command_list_3d_cat.php sorted by A-Z: http://www.blitzmax.com/b3ddocs/command_list_3d_a-z.php You can create more complex 3D-objects by combining the basic primitives. Therefore you need to organize them in dependecies. The biggest object is a parent and smaller parts of the object are added as childrens. If you now move the parents they childrends will stay connected with the parent and all move together like one 3D object: (Screenshot) a tank made of a Cube() as parent and two Spheres()as children: Tank= CreateCube() Tank.ScaleMesh 1.5,0.4,2 Dome=CreateCylinder(8,True,Tank) Dome.MoveEntity 0,.6,0 Dome.ScaleMesh 1.3,0.4,1.5 Gun=CreateCylinder(8,TRUE,Dome) Gun.ScaleMesh 0.2,2,0.2 Gun.TurnEntity 90,0,0 Gun.MoveEntity 0,2,0 A copy of the Gun is added to the camera: Camera = CreateCamera() ... Local Gun2:TEntity=Gun.CopyEntity(Camera) Gun2.MoveEntity 0,-1,1 If you now move the tank, the dome will follow. But it is also possible to turn the dome within the tank: Tank.TurnEntity 0,.5,0 Tank.MoveEntity 0,0,.1 ... Dome.TurnEntity 0,Dir,0 A tank game: to be continued.... |
| ||
Finally a tutorial! Thanks!!! I did try MiniB3D and get an example working, but even though I have used Blitz3D a little I found MiniB3D a bit intimidating. |
| ||
Thank you for creating this. I got it running and am quite happy with it, since I've got some experience with Blitz3D. One question though, does anyone get slight warping or distortion of the shaped in the building flying example? It seems like as I get closer to the objects they kind of stretch to reach out at me. |
| ||
It seems like as I get closer to the objects they kind of stretch to reach out at me. perspective cameras will do that, as it mimics real life lenses. one way to change that is to set the Camera.CameraZoom(2.0). |
| ||
Chapter 5: Easy Collisions Objects in 3D games are not massive by default. Every object can go through the other objects: To prevent from doing that you have to define a invisible "room" around the center of each object. Its dimensions define the border to other objects. As a second step you have to add each object to a "group of same objects". These object later will have the same collision behavior. As a third step to have to define which collisions between which groups should be controlled by the 3D-engine. Step 1: The room The collision room should correlate with the visible shape of the object. The Bowl gets a room like a sphere: MovingObjects%=1 Bowl.CollisionSetup MovingObjects, COLLISION_METHOD_SPHERE Also these types are avaiable: COLLISION_METHOD_SPHERE COLLISION_METHOD_POLYGON COLLISION_METHOD_BOX So our cube gets: Bowl.CollisionSetup MovingObjects, COLLISION_METHOD_BOX Step 2: The Group You can join all object to only one group. Then the collision check has to do a lot, because it has to check every object with all the others. But it would work... It is better to differ the object into groups, because some can never collide. fe.e trees with trees. So it would be useless always to check collisions between them. And some always collide, but it does not matter. f.e. trees and the ground. So think about useful groups to minimize the work the engine will have with collisions. In our sample we only have one group and we call it MovingObjects%=1 local MovingObjects%=1 Bowl.CollisionSetup MovingObjects, COLLISION_METHOD_SPHERE Cube.CollisionSetup MovingObjects, COLLISION_METHOD_BOX Step 3: Collision Pair At last we define a collision check between all moving objects: Collisions MovingObjects,MovingObjects, COLLISION_METHOD_SPHERE, COLLISION_RESPONSE_STOP If a collision happens, there can be various reactions. The object can stop immediately. Or they could change their course and slide along until they do not collide anymore. This reactions are possible: COLLISION_RESPONSE_NONE COLLISION_RESPONSE_STOP COLLISION_RESPONSE_SLIDE COLLISION_RESPONSE_SLIDEXZ All these steps have to be prepared in the Init-Method. Method Init:Void() .... Local MovingObjects%=1 Cube= CreateCube() Cube.PositionEntity -0.5,-2.5,8 Cube.CollisionSetup MovingObjects,COLLISION_METHOD_SPHERE Bowl= CreateSphere() Bowl.PositionEntity 0,5,8 Bowl.EntityColor 255,0,0 Bowl.CollisionSetup MovingObjects,COLLISION_METHOD_SPHERE Collisions MovingObjects,MovingObjects, COLLISION_METHOD_SPHERE, COLLISION_RESPONSE_STOP At the end the engine will automatically check and handle the collision at the command UpdateWorld() in our OnUpdate(): Method OnUpdate%() If KeyHit(KEY_CLOSE) Or KeyHit(KEY_ESCAPE) Then Error "" If Not Started Then Return 0 Cube.TurnEntity 0,1,0 Bowl.MoveEntity 0,-0.1,0 Print Bowl.CountCollisions() UpdateWorld() Return 0 End If you want to be informed about any collision, you could check the CountCollisions() of your object. Box stopps bowl: to be continued..... |
| ||
Chapter 6: Textures If we want to add textures to the entities, we have to pre-load all images first, then load each image into a texture. Then add the texture to a entity and scale the texture on the surface. (Screenshot) For that we need some changes in our basic game code: The Init() needs to ensure, that the loading of the images is already finished, before we try to create textures. Therefore MiniB3D has a function TPixmap.PreLoadPixmap(): Method Init:Void() If Started Then Return 1 If Not TPixmap.PreLoadPixmap(["Ground.png","Sky.png"]) Return Endif Print "Preload finished" .... You call this function with an string array of all needed images. The function will pre-load the images and exit the Init() as long as one image is still missing. For this chapter you need two texture-images. You will find them here: Ground.png: Sky.png: Copy both into your MyGame.data folder! Now we are nearly ready. The rest is easy... Load the textures, scale them and add them to an entity: Floor=CreateGrid(15,15) Floor.ScaleEntity 10,1,10 Floor.PositionEntity -2,-2.5,8 Local Ground:TTexture=LoadTexture( "Ground.png" ) Ground.ScaleTexture .3,.3 Floor.EntityTexture Ground When you create a sky sphere you additional have to flip the mesh to see the texture from "inside" the bowl: Sky=CreateSphere(8) Sky.ScaleEntity 150,50,150 Local Heaven:TTexture=LoadTexture( "Sky.png" ) Sky.EntityTexture Heaven Sky.FlipMesh Sky.EntityFX FXFLAG_FULLBRIGHT Also the sky should not be affected by the light direction, we have to set it's FX to FULLBRIGHT Here is the complete code: landscape with sky: |
| ||
Chapter 7: Sprites Sprites are flat 2D objects in a 3D space. Often they are permanent faced toward the camera. Sprites are used for object, that have the same look from all sides like trees. Or for objects that are connected to the camera. In our sample we will see a forest of flat trees, which will look like 3D trees. And we will see the optic effect of sun reflections in our camera lens. The third sprites shows us, how to bind "buttons" to the camera. (Screenshot) A Sprite has to be preloaded the same way like textures.... Method Init:Void() If Started Then Return If Not TPixmap.PreLoadPixmap(["tree.png","sun1.png","sun2.png","sun3.png","sun4.png","text.png"]) Return Endif .... Here you can download the 6 images: tree.png: sun1.png: (it looks like empty, but isn't!) sun2.png: (it looks like empty, but isn't!) sun3.png: (it looks like empty, but isn't!) sun4.png: (it looks like empty, but isn't!) text.png: After pre-loading the images we can load it into a TSprite Type. Class Game Extends App Field Camera:TCamera Field Light:TLight Field Floor:TMesh Field Sun1:TSprite, Sun2:TSprite, Sun3:TSprite, Sun4:TSprite Field Tree:TSprite, Button:TSprite Field Started:Bool .... Method Init:Void() ..... Tree = LoadSprite("tree.png",2) ..... Button=LoadSprite("text.png",2,Camera) ..... Sun1 = LoadSprite("sun1.png",2,Camera) ..... .... A forest: To produce a lot of objects of the same image we can copy the sprite entity: Method Init:Void() ..... For Local i%=0 To 200 Local locTree:TEntity = CopyEntity(Tree) Local YScale#=Rnd(.3,1.5) Local x#,y# x=Rnd(30) y=Rnd(30) locTree.PositionEntity x,YScale,y locTree.ScaleEntity Rnd(0.3,1),YScale,1 locTree.EntityColor Rnd(0,255),Rnd(230,255),Rnd(150,205) Next You handle those copies like independent entities and define a different size, color or position for each copy of the original sprite. Lens reflections We add four sprites in front of our camera and will move them left or right depending of the camera's direction. This causes a lens reflection effect. Method Init:Void() ..... Sun1 = LoadSprite("sun1.png",2,Camera) Sun1.EntityFX 9 Sun1.ScaleSprite 2,2 Sun1.EntityColor 255,255,242 Sun2 = LoadSprite("sun2.png",2,Camera) Sun2.EntityFX 9 Sun2.ScaleSprite .4,.4 Sun2.EntityColor 255,255,220 Sun3 = LoadSprite("sun3.png",2,Camera) Sun3.EntityFX 9 Sun3.ScaleSprite .7,.7 Sun3.EntityColor 255,255,200 Sun4 = LoadSprite("sun4.png",2,Camera) Sun4.EntityFX 9 Sun4.ScaleSprite .7,.7 Sun4.EntityColor 255,255,180 They are moved in our MovingCamera() method: Method MovingCamera:Void() ..... Local Angle#=((EntityYaw(Camera)+36000) Mod 360)-180 Local alpha#=1.0-Abs(Angle)/60.0 Sun1.EntityAlpha alpha Sun2.EntityAlpha alpha Sun3.EntityAlpha alpha Sun4.EntityAlpha alpha Sun1.PositionEntity 0.05*Angle,1.5,2 Sun2.PositionEntity -0.017*Angle,-0.8,2 Sun3.PositionEntity 0,-0.8,2 Sun4.PositionEntity -.034*Angle,-1,2 A control "button" Closed to our camera there shall be a text. The nearest possible distance between objects and camera is 1. So we put this texture on Z=1.01: Method Init:Void() ..... Button=LoadSprite("text.png",2,Camera) Button.ScaleEntity 1.01,.1,0.01 Button.MoveEntity 0,-0.7, 1.01 the complete code Sprite.monkey: |
| ||
Another excellent tutorial tutorial, thank you! :) |
| ||
Just tried the Sprite example and is not working properly. The GLFW target crashed my nvidia drivers and html5 returned: WebGL 1.0 (OpenGL ES 2.0 Chromium) **WEBGL 1 ..FullShader success Monkey Runtime Error : Rendering operations can only be performed inside OnRender C:/MonkeyPro69/modules/mojo/graphics.monkey<48> C:/MonkeyPro69/modules/mojo/graphics.monkey<387> D:/projects/testandother/Project3D/main.monkey<123> C:/MonkeyPro69/modules/mojo/app.monkey<67> |
| ||
Line 123 is inside the OnRender(). It was a relict from some bug tests. At me it worked perfect also with this line. To be on the save side, I now removed the line. Please copy and paste the new code and test it again. I tested the new sample code with my Monkey V69 and MiniB3D V38 (new version from yesterday) on target HTML5... And it works. Did you perhaps add anything? The original code renders 100% nothing outside the OnRender(). What about your updates? Do you work with the latest version? What about the other chapter's codes? Are they working? Only for a test I would suggest to return OnUpdate() and see what happens: Method OnUpdate%() If KeyHit(KEY_CLOSE) Or KeyHit(KEY_ESCAPE) Then Error "" If Not Started Then Return 0 Return 0 End |
| ||
Yeah, it seems this was the issue:DrawRect 100,100,200,200 But I found something new. Testing on html5 it is working great. I compiled and tried on Android. My floor mesh is white and I do not see the button. Any idea why? |
| ||
Thank you for your report... I think I need a little bit more information. Which version of Monkey? ...Android? ...MiniB3D? A white floor sounds like a module bug. I think you should send a copy of your bug report to the minib3d thread too: http://www.monkeycoder.co.nz/Community/posts.php?topic=5016 The button problem can be caused, because it has a very closed position to the camera. Can you please test this: Button=LoadSprite("text.png",2,Camera) Button.ScaleEntity 1.01,.1,0.01 Button.MoveEntity 0,-0.7, 2 '<-new value=2 if this works, try also this values: 1.50 then 1.10 then 1.05 then 1.02 |
| ||
I have Monkey 69 version with the last MiniB3D. I ran the software on a Samsung tablet (GT-N8013) with Android 4.1.1 Regarding the buttons: 2 and 1.5 are working ok. Although some of the trees will come between text and camera :) |
| ||
Midimaster, any chance of some more tutorials please? Animation for example? |
| ||
I'm having problems with collisions, a sphere dips into the corners of a cube. Does this in the collision sample too? |
| ||
I'm having problems with collisions, a sphere dips into the corners of a cube. working on that now, but also make sure your collisions are using "box" or "polygon" and not "sphere" Collisions( 1, 2, COLLISION_METHOD_BOX, COLLISION_RESPONSE_SLIDEXZ ) |
| ||
More tutorials are coming soon. I have a tutorial about loading a 3D multitexture landscape from a *.B3D file nearly ready, but at the moment Adam does some fundamental updates because of that. |
| ||
@Adam: ah, thank you Adam. :) @Midimaster: Great stuff. I'll look forward to that too! Again, thanks guys. |
| ||
i have the minib3d in my folder (D:\MonkeyPro\modules\minib3d) but a get an error on compilation the fist sample : "D:/MonkeyPro/bin/transcc_winnt" -target=Glfw_Game -config=Release -run "D:/DEV/momo2/momo44.monkey" TRANS monkey compiler V1.56 Parsing... D:/DEV/momo2/momo44.monkey<3> : Error : Module 'minib3d' not found. Done. |
| ||
1. Is "D:\MonkeyPro\" the folder, where your "Monkeyxx.Exe" is? 2. Can you see "minib3d.monkey" inside the folder "D:\MonkeyPro\modules\minib3d\"? Or is there another sub-folder? 3. Did you already try to destroy the "momo44.build" folder in D:/DEV/momo2/"? 4. Have a look on the right side of the code editor TED: "Browser - Projects - Monkeyxx - modules -". Can you see here the minib3d folder? 5. Did you try one of the examples of "D:\MonkeyPro\modules\minib3d\examples" or the tutorial chapter 2 example? 6. If you tested 1. to 5., write a bug report post to this thread: http://www.monkeycoder.co.nz/Community/posts.php?topic=5703 |
| ||
1 : yes 2 : no, minib3d.monkey don't exsit in any folder 3 : yes 4 : yes 5 : no, its only *.bmx |
| ||
hey, it'es ok, the *.zip was not the good. ? Thanks, it's closed. |
| ||
WOW, this is an awesome tutorial. I'm glad I found this. I got things going in minutes. I new to 3D game development! |
| ||
You got this to run? I constantly get errors like "Native OS Module not implemented", or "Method TColTree.new() is private." and can't even get the second tutorial to run. |
| ||
That's an old error, that my lazy butt needs to fix. For now, you can comment out the "Import OS" line, and in file "tcoltree.monkey" the New Method can be moved above into the public area. |
| ||
Thanks! My own lazy ass actually solved these issues already, but the HTML5 target still has an error: "Unable to find overload for glDrawElements(Int,Int,Int,DataBuffer)." Edit: Okay, seems like enclosing the offensive lines with #if TARGET<>"html5" ... #end helps! |
| ||
Thanks for the tutorials! I managed to get the errors to go away in HTML5, but nothing renders. :-( I had never checked MiniB3D, and I have to say it looks pretty awesome, but developing for GLFW only is pretty slow (takes a few seconds to compile even a simple example). Any chance this will ever be updated/maintained? Maybe updated to Mojo2? Seriously, I'd pay money for this! Leo. |
| ||
I'm thinking about it. |