Dark Basic Pro Vol 2
Dark Basic Pro Vol 2
Dark Basic Pro Vol 2
Volume 2
A Self-Study Guide to Games Programming
Alistair Stewart
Digital Skills
Milton
Barr
Girvan
Ayrshire
KA26 9TY
www.digital-skills.co.uk
Copyright © Alistair Stewart 2006
All brand names and product names are trademarks of their respective
companies and have been capitalised throughout the text.
ISBN-10 : 1-874107-09-2
ISBN-13 : 978-1-874107-09-5
Chapter 31 3D Primitives
3D Primitives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 756
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 756
Creating a Cube . . . . . . . . . . . . . . . . . . . . . . . . . . . 756
The MAKE OBJECT CUBE Statement . . . . . . . . . . . . . 756
Creating Other Primitives . . . . . . . . . . . . . . . . . . . . . . . 757
The MAKE OBJECT BOX Statement . . . . . . . . . . . . . . 757
The MAKE OBJECT SPHERE Statement . . . . . . . . . . . . 758
The MAKE OBJECT CYLINDER Statement . . . . . . . . . . . 759
The MAKE OBJECT CONE Statement . . . . . . . . . . . . . 760
The MAKE OBJECT PLAIN Statement . . . . . . . . . . . . . 760
The MAKE OBJECT TRIANGLE Statement . . . . . . . . . . . 761
Positioning an Object . . . . . . . . . . . . . . . . . . . . . . . . . 762
The POSITION OBJECT Statement . . . . . . . . . . . . . . . 762
The MOVE OBJECT Statement . . . . . . . . . . . . . . . . . 764
Rotating Objects - Absolute Rotation . . . . . . . . . . . . . . . . . 765
The XROTATE OBJECT Statement . . . . . . . . . . . . . . . 766
The YROTATE OBJECT Statement . . . . . . . . . . . . . . . 767
The ZROTATE OBJECT Statement . . . . . . . . . . . . . . . 767
The ROTATE OBJECT Statement . . . . . . . . . . . . . . . . 768
The SET OBJECT ROTATION Statement . . . . . . . . . . . 769
Rotating Objects - Relative Rotation . . . . . . . . . . . . . . . . . 769
The PITCH OBJECT Statement . . . . . . . . . . . . . . . . . 770
The TURN OBJECT Statement . . . . . . . . . . . . . . . . . 770
The ROLL OBJECT Statement . . . . . . . . . . . . . . . . . 771
The POINT OBJECT Statement . . . . . . . . . . . . . . . . . 771
The MOVE OBJECT distance Statement . . . . . . . . . . . . 772
The FIX OBJECT PIVOT Statement . . . . . . . . . . . . . . . 773
Resizing Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . 775
The SCALE OBJECT Statement . . . . . . . . . . . . . . . . . 775
Showing and Hiding Objects . . . . . . . . . . . . . . . . . . . . . 776
The HIDE OBJECT Statement . . . . . . . . . . . . . . . . . . 776
The SHOW OBJECT Statement . . . . . . . . . . . . . . . . . 776
The DELETE OBJECT Statement . . . . . . . . . . . . . . . . 777
The DELETE OBJECTS Statement . . . . . . . . . . . . . . . 777
Copying a 3D Object . . . . . . . . . . . . . . . . . . . . . . . . . 778
The CLONE OBJECT Statement . . . . . . . . . . . . . . . . 778
The INSTANCE OBJECT Statement . . . . . . . . . . . . . . 779
Retrieving Data on 3D Objects . . . . . . . . . . . . . . . . . . . . 779
The OBJECT EXIST Statement . . . . . . . . . . . . . . . . . 779
The OBJECT POSITION Statement . . . . . . . . . . . . . . 780
The OBJECT VISIBLE Statement . . . . . . . . . . . . . . . . 780
The OBJECT SIZE Statement . . . . . . . . . . . . . . . . . . 781
The OBJECT ANGLE Statement . . . . . . . . . . . . . . . . 781
Controlling an Object's Rotation Using the Mouse . . . . . . . . . . 782
Wireframe and Culling . . . . . . . . . . . . . . . . . . . . . . . . 783
The SET OBJECT WIREFRAME Statement . . . . . . . . . . 783
The SET OBJECT CULL Statement . . . . . . . . . . . . . . . 784
Storage Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . 785
The SET GLOBAL OBJECT CREATION Statement . . . . . . 785
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 786
Merging Primitives . . . . . . . . . . . . . . . . . . . . . . . . . . . . 788
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 788
The Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . 788
The PERFORM CSG UNION Statement . . . . . . . . . . . . 788
The PERFORM CSG DIFFERENCE Statement . . . . . . . . . 790
The PERFORM CSG INTERSECTION Statement . . . . . . . 790
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791
Solutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 792
Chapter 32 Texturing
Adding Texture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 798
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 798
Loading a Texture Image . . . . . . . . . . . . . . . . . . . . . . . 798
Using the Image as a Texture . . . . . . . . . . . . . . . . . . . . 798
The TEXTURE OBJECT Statement . . . . . . . . . . . . . . . 798
Mipmaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 799
The LOAD IMAGE Statement Again . . . . . . . . . . . . . . . 800
Tiling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801
The SCALE OBJECT TEXTURE Statement . . . . . . . . . . . 802
Seamless Tiling . . . . . . . . . . . . . . . . . . . . . . . . . . . 804
Video Texture . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805
The PLAY ANIMATION TO IMAGE Statement . . . . . . . . . 805
Other Texture Effects . . . . . . . . . . . . . . . . . . . . . . . . . 807
The SET OBJECT TEXTURE Statement . . . . . . . . . . . . 807
The SCROLL OBJECT TEXTURE Statement . . . . . . . . . . 808
The SET OBJECT TRANSPARENCY Statement . . . . . . . . 810
The SET DETAIL MAPPING ON Statement . . . . . . . . . . . 811
The SET OBJECT FILTER Statement . . . . . . . . . . . . . . 813
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 814
Other Visual Effects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815
Changing Colour and Transparency . . . . . . . . . . . . . . . . . 815
The COLOR OBJECT Statement . . . . . . . . . . . . . . . . 815
The GHOST OBJECT ON Statement . . . . . . . . . . . . . . 816
The GHOST OBJECT OFF Statement . . . . . . . . . . . . . 817
The FADE OBJECT Statement . . . . . . . . . . . . . . . . . 817
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 819
Images with an Alpha Channel . . . . . . . . . . . . . . . . . . . . . . 820
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 820
Using Images with an Alpha Channel . . . . . . . . . . . . . . . . 820
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821
Creating a Complex 3D Shape . . . . . . . . . . . . . . . . . . . . . . 822
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 822
Designing the Castle . . . . . . . . . . . . . . . . . . . . . . . . . 822
Gathering the Components . . . . . . . . . . . . . . . . . . . 823
Creating the Code . . . . . . . . . . . . . . . . . . . . . . . . . . 823
The Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824
Sky Spheres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 828
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 830
Solutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831
Chapter 33 Cameras
Camera Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 836
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 836
Positioning the Camera . . . . . . . . . . . . . . . . . . . . . . . 836
The POSITION CAMERA Statement . . . . . . . . . . . . . . 836
The MOVE CAMERA Statement . . . . . . . . . . . . . . . . . 837
Changing the Viewpoint . . . . . . . . . . . . . . . . . . . . . . . 838
The POINT CAMERA Statement . . . . . . . . . . . . . . . . 838
The ROTATE CAMERA Statement . . . . . . . . . . . . . . . 838
The SET CAMERA ROTATION Statement . . . . . . . . . . . 840
The XROTATE CAMERA Statement . . . . . . . . . . . . . . 840
The YROTATE CAMERA Statement . . . . . . . . . . . . . . 841
The ZROTATE CAMERA Statement . . . . . . . . . . . . . . 841
The PITCH CAMERA Statement . . . . . . . . . . . . . . . . 842
The TURN CAMERA Statement . . . . . . . . . . . . . . . . . 842
The ROLL CAMERA Statement . . . . . . . . . . . . . . . . . 843
Retrieving Camera Data . . . . . . . . . . . . . . . . . . . . . . . 844
The CAMERA POSITION Statement . . . . . . . . . . . . . . 844
The CAMERA ANGLE Statement . . . . . . . . . . . . . . . . 844
Modifying Camera Characteristics . . . . . . . . . . . . . . . . . . 845
The SET CAMERA VIEW Statement . . . . . . . . . . . . . . 845
The SET CAMERA ASPECT Statement . . . . . . . . . . . . . 846
The SET CAMERA FOV Statement . . . . . . . . . . . . . . . 847
The SET CAMERA RANGE Statement . . . . . . . . . . . . . 848
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 849
Controlling Camera Movement . . . . . . . . . . . . . . . . . . . . . . 851
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 851
Automatic Camera Placement . . . . . . . . . . . . . . . . . . . . 851
The AUTOCAM Statement . . . . . . . . . . . . . . . . . . . . 851
Following the Action . . . . . . . . . . . . . . . . . . . . . . . . . 852
The SET CAMERA TO FOLLOW Statement . . . . . . . . . . 853
Giving the Player Control of the Camera . . . . . . . . . . . . . . . 856
The CONTROL CAMERA USING ARROWKEYS Statement . . 856
The AUTOMATIC CAMERA COLLISION Statement . . . . . . 858
Controlling the Camera with the Mouse . . . . . . . . . . . . . 859
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 862
Multiple Cameras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
Using Additional Cameras . . . . . . . . . . . . . . . . . . . . . . 863
The MAKE CAMERA Statement . . . . . . . . . . . . . . . . . 863
The COLOR BACKDROP Statement . . . . . . . . . . . . . . 864
The BACKDROP Statement . . . . . . . . . . . . . . . . . . . 864
The SET CURRENT CAMERA Statement . . . . . . . . . . . . 865
The DELETE CAMERA Statement . . . . . . . . . . . . . . . 866
Switching Between Cameras . . . . . . . . . . . . . . . . . . 866
Multiple Camera Output . . . . . . . . . . . . . . . . . . . . . 868
The CLEAR CAMERA VIEW Statement . . . . . . . . . . . . . 869
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 870
Advanced Camera Techniques . . . . . . . . . . . . . . . . . . . . . . 871
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 871
The Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . 871
The SET CAMERA TO IMAGE Statement . . . . . . . . . . . . 871
The SET CAMERA TO OBJECT ORIENTATION Statement . . 873
The SET OBJECT TO CAMERA ORIENTATION Statement . . 873
The LOCK OBJECT Statement . . . . . . . . . . . . . . . . . 874
The SET VECTOR3 TO CAMERA POSITION Statement . . . . 875
The SET VECTOR3 TO CAMERA ROTATION Statement . . . 876
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876
Solutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 878
Chapter 34 Lighting
Lighting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 886
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 886
Types of Lighting . . . . . . . . . . . . . . . . . . . . . . . . . . . 886
Ambient Lighting . . . . . . . . . . . . . . . . . . . . . . . . . 886
Point Lighting . . . . . . . . . . . . . . . . . . . . . . . . . . . 886
Spot Lighting . . . . . . . . . . . . . . . . . . . . . . . . . . . 886
Directional Lighting . . . . . . . . . . . . . . . . . . . . . . . . 886
Lighting in DarkBASIC Pro . . . . . . . . . . . . . . . . . . . . . . 887
The HIDE LIGHT Statement . . . . . . . . . . . . . . . . . . . 887
The SHOW LIGHT Statement . . . . . . . . . . . . . . . . . . 888
The SET AMBIENT LIGHT Statement . . . . . . . . . . . . . . 888
The COLOR AMBIENT LIGHT Statement . . . . . . . . . . . . 889
The MAKE LIGHT Statement . . . . . . . . . . . . . . . . . . 889
The DELETE LIGHT Statement . . . . . . . . . . . . . . . . . 890
The COLOR LIGHT Statement . . . . . . . . . . . . . . . . . 890
The POSITION LIGHT Statement . . . . . . . . . . . . . . . . 891
The SET LIGHT RANGE Statement . . . . . . . . . . . . . . . 891
The SET SPOT LIGHT Statement . . . . . . . . . . . . . . . . 892
The SET DIRECTIONAL LIGHT Statement . . . . . . . . . . . 892
The SET POINT LIGHT Statement . . . . . . . . . . . . . . . 893
The POINT LIGHT Statement . . . . . . . . . . . . . . . . . . 893
The ROTATE LIGHT Statement . . . . . . . . . . . . . . . . . 895
The SET LIGHT TO OBJECT POSITION Statement . . . . . . 895
The SET LIGHT TO OBJECT ORIENTATION Statement . . . . 897
Retrieving Light Data . . . . . . . . . . . . . . . . . . . . . . . . . 898
The LIGHT EXIST Statement . . . . . . . . . . . . . . . . . . 898
The LIGHT VISIBLE Statement . . . . . . . . . . . . . . . . . 899
The LIGHT RANGE Statement . . . . . . . . . . . . . . . . . 899
The LIGHT TYPE Statement . . . . . . . . . . . . . . . . . . . 899
The LIGHT POSITION Statement . . . . . . . . . . . . . . . . 900
The LIGHT DIRECTION Statement . . . . . . . . . . . . . . . 900
Fog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 901
The FOG Statement . . . . . . . . . . . . . . . . . . . . . . . 901
The FOG COLOR Statement . . . . . . . . . . . . . . . . . . 902
The FOG DISTANCE Statement . . . . . . . . . . . . . . . . . 902
The SET OBJECT FOG Statement . . . . . . . . . . . . . . . 903
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 904
Solutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 907
Chapter 35 Meshes and Limbs
Meshes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 912
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 912
Handling Meshes . . . . . . . . . . . . . . . . . . . . . . . . . . . 912
The MAKE MESH FROM OBJECT Statement . . . . . . . . . 912
The SAVE MESH Statement . . . . . . . . . . . . . . . . . . . 913
The LOAD MESH Statement . . . . . . . . . . . . . . . . . . 914
The MAKE OBJECT Statement . . . . . . . . . . . . . . . . . 914
The DELETE MESH Statement . . . . . . . . . . . . . . . . . 915
The MESH EXIST Statement . . . . . . . . . . . . . . . . . . 915
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 916
Limbs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 917
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 917
Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . 917
The ADD LIMB Statement . . . . . . . . . . . . . . . . . . . . 917
The MAKE OBJECT FROM LIMB Statement . . . . . . . . . . 919
The OFFSET LIMB Statement . . . . . . . . . . . . . . . . . . 920
The ROTATE LIMB Statement . . . . . . . . . . . . . . . . . . 920
The SCALE LIMB Statement . . . . . . . . . . . . . . . . . . 922
The COLOR LIMB Statement . . . . . . . . . . . . . . . . . . 922
The TEXTURE LIMB Statement . . . . . . . . . . . . . . . . . 923
The SCALE LIMB TEXTURE Statement . . . . . . . . . . . . . 925
The SCROLL LIMB TEXTURE Statement . . . . . . . . . . . . 927
The HIDE LIMB Statement . . . . . . . . . . . . . . . . . . . . 927
The SHOW LIMB Statement . . . . . . . . . . . . . . . . . . . 928
The REMOVE LIMB Statement . . . . . . . . . . . . . . . . . 928
The LINK LIMB Statement . . . . . . . . . . . . . . . . . . . . 928
The CHANGE MESH Statement . . . . . . . . . . . . . . . . . 931
The GLUE OBJECT TO LIMB Statement . . . . . . . . . . . . 931
The UNGLUE OBJECT Statement . . . . . . . . . . . . . . . 934
The SET LIMB SMOOTHING Statement . . . . . . . . . . . . 934
Creating Doors . . . . . . . . . . . . . . . . . . . . . . . . . . . . 935
Retrieving Limb Data . . . . . . . . . . . . . . . . . . . . . . . . . 936
The LIMB EXIST Statement . . . . . . . . . . . . . . . . . . . 936
The LIMB VISIBLE Statement . . . . . . . . . . . . . . . . . . 937
The LIMB OFFSET Statement . . . . . . . . . . . . . . . . . . 937
The LIMB SCALE Statement . . . . . . . . . . . . . . . . . . 938
The LIMB ANGLE Statement . . . . . . . . . . . . . . . . . . 939
The LIMB POSITION Statement . . . . . . . . . . . . . . . . . 939
The LIMB DIRECTION Statement . . . . . . . . . . . . . . . . 940
The PERFORM CHECKLIST FOR OBJECT LIMBS Statement . 944
The LIMB NAME$ Statement . . . . . . . . . . . . . . . . . . 945
The LIMB TEXTURE Statement . . . . . . . . . . . . . . . . . 946
The LIMB TEXTURE NAME Statement . . . . . . . . . . . . . 946
The CHECK LIMB LINK Statement . . . . . . . . . . . . . . . 947
Saving a Model in DBO Format . . . . . . . . . . . . . . . . . . . 947
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . 947
The DBO File Format . . . . . . . . . . . . . . . . . . . . . . 948
Creating an Elevator Model . . . . . . . . . . . . . . . . . . . 948
The SAVE OBJECT Statement . . . . . . . . . . . . . . . . . 949
The LOAD OBJECT Statement . . . . . . . . . . . . . . . . . 950
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 951
Solutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 953
Chapter 38 Solitaire
Solitaire - The Board Game . . . . . . . . . . . . . . . . . . . . . . . . 994
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 994
The Equipment . . . . . . . . . . . . . . . . . . . . . . . . . . 994
The Aim . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 994
The Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . 994
Creating a Computer Version of the Game . . . . . . . . . . . . . 994
User Controls . . . . . . . . . . . . . . . . . . . . . . . . . . 994
Game Responses . . . . . . . . . . . . . . . . . . . . . . . . 995
Screen Layout . . . . . . . . . . . . . . . . . . . . . . . . . . 995
Media Used . . . . . . . . . . . . . . . . . . . . . . . . . . . 995
Data Structures . . . . . . . . . . . . . . . . . . . . . . . . . 996
Adding SetUpScreen() . . . . . . . . . . . . . . . . . . . . . . 999
Adding SetUpGame() . . . . . . . . . . . . . . . . . . . . . . 1000
Adding CreateBoard() . . . . . . . . . . . . . . . . . . . . . . 1001
Adding CreateInternalBoard() . . . . . . . . . . . . . . . . . . 1001
Adding CreateMarbles() . . . . . . . . . . . . . . . . . . . . . 1002
Adding CreateSelector() . . . . . . . . . . . . . . . . . . . . . 1002
Adding SetUpHelp() . . . . . . . . . . . . . . . . . . . . . . . 1003
Adding GetPlayerMove() . . . . . . . . . . . . . . . . . . . . . 1004
Adding MoveSelector() . . . . . . . . . . . . . . . . . . . . . . 1006
Adding SelectMarble() . . . . . . . . . . . . . . . . . . . . . . 1007
Adding SelectPit() . . . . . . . . . . . . . . . . . . . . . . . . 1008
Adding IsValidMove() . . . . . . . . . . . . . . . . . . . . . . 1008
Adding MoveMarble() . . . . . . . . . . . . . . . . . . . . . . 1008
Adding SelectHelpPage() . . . . . . . . . . . . . . . . . . . . 1009
Using the Mouse . . . . . . . . . . . . . . . . . . . . . . . . . . . 1009
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1009
Updating the Program . . . . . . . . . . . . . . . . . . . . . . 1010
Suggested Enhancements . . . . . . . . . . . . . . . . . . . . . . 1013
Solutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1015
Chapter 40 Collisions
Object Collisions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1068
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1068
Object Collision . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1068
The OBJECT HIT Statement . . . . . . . . . . . . . . . . . . . 1069
The OBJECT COLLISION Statement . . . . . . . . . . . . . . 1070
The SET OBJECT COLLISION Statement . . . . . . . . . . . 1070
The SET GLOBAL COLLISION Statement . . . . . . . . . . . 1071
How Collision Detection Works . . . . . . . . . . . . . . . . . . . . 1071
The SHOW OBJECT BOUNDS Statement . . . . . . . . . . . 1072
The HIDE OBJECT BOUNDS statement . . . . . . . . . . . . 1072
Modifying Collision Detection . . . . . . . . . . . . . . . . . . . . . 1074
The SET OBJECT COLLISION TO SPHERES Statement . . . 1074
The SET OBJECT RADIUS Statement . . . . . . . . . . . . . 1074
The OBJECT COLLISION RADIUS Statement . . . . . . . . . 1075
The OBJECT COLLISION CENTER Statement . . . . . . . . . 1075
The SET OBJECT COLLISION TO BOXES Statement . . . . . 1076
The SET OBJECT COLLISION TO POLYGONS Statement . . 1076
The MAKE OBJECT COLLISION BOX Statement . . . . . . . 1077
The GET OBJECT COLLISION Statement . . . . . . . . . . . 1080
The DELETE OBJECT COLLISION BOX Statement . . . . . . 1082
The AUTOMATIC OBJECT COLLISION Statement . . . . . . . 1082
The INTERSECT OBJECT Statement . . . . . . . . . . . . . . 1083
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1085
Static Collisions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1087
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1087
Creating and Using Static Collision Boxes . . . . . . . . . . . . . . 1087
The MAKE STATIC COLLISION BOX Statement . . . . . . . . 1087
The GET STATIC COLLISION HIT Statement . . . . . . . . . 1087
The GET STATIC COLLISION Statement . . . . . . . . . . . 1089
The STATIC LINE OF SIGHT Statement . . . . . . . . . . . . 1093
The STATIC LINE OF SIGHT Coordinates Statement . . . . . 1095
Static Collision Boxes and the Camera . . . . . . . . . . . . . . . 1096
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1096
Solutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1098
Chapter 41 Particles
Particles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1102
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1102
Creating Particles . . . . . . . . . . . . . . . . . . . . . . . . . . 1102
The MAKE PARTICLES Statement . . . . . . . . . . . . . . . 1102
The HIDE PARTICLES Statement . . . . . . . . . . . . . . . . 1103
The SHOW PARTICLES Statement . . . . . . . . . . . . . . . 1104
The DELETE PARTICLES Statement . . . . . . . . . . . . . . 1104
The POSITION PARTICLES Statement . . . . . . . . . . . . . 1104
The POSITION PARTICLE EMISSIONS Statement . . . . . . . 1105
The ROTATE PARTICLES Statement . . . . . . . . . . . . . . 1106
The COLOR PARTICLES Statement . . . . . . . . . . . . . . 1107
The SET PARTICLE EMISSIONS Statement . . . . . . . . . . 1108
The SET PARTICLE VELOCITY Statement . . . . . . . . . . . 1109
The SET PARTICLE GRAVITY Statement . . . . . . . . . . . 1110
The SET PARTICLE CHAOS Statement . . . . . . . . . . . . 1110
The SET PARTICLE SPEED Statement . . . . . . . . . . . . . 1111
The SET PARTICLE FLOOR Statement . . . . . . . . . . . . . 1112
The SET PARTICLE LIFE Statement . . . . . . . . . . . . . . 1113
The GHOST PARTICLES ON Statement . . . . . . . . . . . . 1113
The GHOST PARTICLES OFF Statement . . . . . . . . . . . . 1114
Retrieving Data on a Particles Object . . . . . . . . . . . . . . . . 1114
The PARTICLES EXIST Statement . . . . . . . . . . . . . . . 1114
The PARTICLES POSITION Statement . . . . . . . . . . . . 1115
Particles Statements that use Vectors . . . . . . . . . . . . . . . . 1116
The SET VECTOR3 TO PARTICLES POSITION Statement . . 1116
The SET VECTOR3 TO PARTICLES ROTATION Statement . . 1116
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1116
Other Types of Particles . . . . . . . . . . . . . . . . . . . . . . . . . 1118
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1118
The Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . 1118
The MAKE SNOW PARTICLES Statement . . . . . . . . . . . 1118
The MAKE FIRE PARTICLES Statement . . . . . . . . . . . . 1119
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1120
Examples of Using Particles . . . . . . . . . . . . . . . . . . . . . . . 1121
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1121
A Roman Candle . . . . . . . . . . . . . . . . . . . . . . . . . . . 1121
A Spaceship . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1122
A Dungeon Torch . . . . . . . . . . . . . . . . . . . . . . . . . . . 1122
Solutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1124
Chapter 50 Shaders
Shaders and FX Files . . . . . . . . . . . . . . . . . . . . . . . . . . . 1376
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1376
Vertex Shader . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1376
Pixel Shader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1376
FX Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1377
Graphics Card Check Statements . . . . . . . . . . . . . . . . . . 1377
The GET MAXIMUM VERTEX SHADER VERSION Statement . 1377
The GET MAXIMUM PIXEL SHADER VERSION Statement . . 1377
FX Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1378
The LOAD EFFECT Statement . . . . . . . . . . . . . . . . . 1378
The EFFECT EXIST Statement . . . . . . . . . . . . . . . . . 1378
The PERFORM CHECKLIST FOR EFFECT ERRORS Statement . . . 1379
I would like to thank all those who helped me prepare the final draft of this book.
In particular, Virginia Marshall who proof-read the original script and Michael Kerr
who did an excellent job of checking the technical contents. Mark Armstrong
researched all the difficult bits for me and produced almost as much in the way of
notes as is in this final text.
Any errors that remain are probably due to the usual extra paragraphs I added after
all the proof-reading was complete!
Thanks also to The Game Creators Ltd for producing an excellent piece of software
- DarkBASIC Professional.
Many of the 3D models and textures are from The Game Creators Dark Matter 1
package and used with their kind permission.
Finally, thank you to every one of you who has bought this book. Any constructive
comments would be most welcome.
Email me at alistair@digital-skills.co.uk.
Introduction
Welcome to the second volume of a book that I hope is a little different from any
other you've come across before. Instead of just telling you about software design
and programming, it makes you get involved. There's plenty of work for you to do
since the book is full of exercises - most of them programming exercises - but you
also get a full set of solutions, just in case you get stuck!
If you've worked your way through Volume 1, then you should have gained a good
grounding in, not only DarkBASIC Pro, but also professional programming skills.
Most of Volume 2 is dedicted to 3D graphics but there a few other interesting topics
such as network programming and how to create your own DarkBASIC commands.
Learn by Doing
The only way to become a programming expert is to practice. No one ever learned
any skill by just reading about it! Hence, this is not a text book where you can just
sit back in a passive way and read from cover to cover whilst sitting in your favourite
chair. Rather it is designed as a teaching package in which you will do most of the
work.
The tasks embedded in the text are included to test your understanding of what has
gone before and as a method of helping you retain the knowledge you have gained.
It is therefore important that you tackle each task as you come to it. Also, many of
the programming exercises are referred to, or expanded, in later pages so it is
important that you are familar with the code concerned.
At this stage you'll also need some programming skills and a basic knowledge of
DarkBASIC Pro.
2. Re-read the chapter. This time take things slowly; make notes and
summaries of the material you are reading (even if you understand the
material, making notes helps to retain the facts in your long-term
memory); re-read any parts you are unclear about.
4. As you progress through the book, go back and re-read earlier chapters,
since you will often get something new from them as your knowledge
increases.
Syntax Diagrams
The format of each statement is explained using a syntax diagram. Raised tiles
represent keywords of the language while sunken tiles are parts of the statement for
which you are free to create your own values. Parts within square brackets are
optional while braces represent a choice of options. Statements that return a value
show this using an arrowed line and the type of value returned.
Z An arrowed line
indicates that the
statement returns a value
real
might appear as
In such cases you should enter the code as a single line when creating a DarkBASIC
Pro program.
30
3D Coordinate System
3D Primitives
3D Vectors
Cameras
Lights
Wireframe Models
World Units
In this chapter we'll get a broad view of the 3D world created by computers. We'll
cover the basic concepts and define some of the terms. Many of these concepts will
be explained in greater detail in later chapters as we discover how DarkBASIC Pro
implements many of these ideas.
FIG-30.1
+y
The Axes used in 3D
+z
-x
+x
-z
-y
In the figure above, the axes have been skewed slightly to give a better perspective
In reality the x-axis runs across the screen, the y-axis runs up and down, and the
z-axis points directly out of the screen (-z) and into the screen (+z) (see FIG-30.2).
The computer
screen
The viewer
+z
-x
+x
-z
-y
Planes
In mathematics, a plane is a flat surface with only two dimensions. 3D space has
three main planes: the X-Y plane, the X-Z plane and the Y-Z plane (see FIG-30.3).
+y
FIG-30.3
The
The Main 3D Planes
X-Y Plane
+z
-x
+x
-z
-y
+y
The
X-Z Plane
+z
-x
+x
-z
-y
+y
The
Y-Z Plane
+z
-x
+x
-z
-y
These three planes are important since each divides space into two equally sized
areas. The X-Y plane splits space with one half to the front, the other half to the
back. The X-Z planes splits space into above and below sections, and the Y-Z plane
splits space into left and right sections.
With all three planes in place, space is split into eight equally-sized sections. Each
of these sections is known as an octant.
Of course, not all planes lie on axes; there are an infinite number of planes, some
parallel to the main planes, others at angles to those planes, but it is the main planes
that will be useful in many of the calculations required when determining the
position of an object in 3D space.
Points
To specify the position of a point in 3D space we state its distance from the origin
along all three axes in the order, x, y, z (see FIG-30.4).
FIG-30.4 +y
Determining the Position of a
Point in 3D Space A point in
space
+z
-x
+x
1
-z Measure the point’s
distance out from the
origin along the x-axis
-y
+y
+z
2
-xMeasure the point’s
distance out from the
origin along the y-axis
+x
-z
-y
+z
-x
3 +x
-xMeasure the point’s
distance out from the
origin-z
along the z-axis
-y
We might, for example, state that point p is at the position (8,12,5) meaning that
point p is 8 units along the x-axis, 12 units along the y-axis and 5 units along the
z-axis.
World Units
Distances are measured in units. These units have no relationship to real-life
measurements such as centimetres or inches. Instead, objects are constructed in such
a way as to be the correct size relative to other objects. For example, if we make a
human character 6 units high, then a simple house might be 18 to 25 units high. Of
course, if you wish, you can think of 1 unit being the equivalent of a real distance.
The scale you choose will depend on the context; when creating a world with an
ant as the main character, 1 unit might be equivalent to a millimetre, while a truly
interstellar game might make 1 unit equivalent to 1 light year.
Local Axes
Every 3D object we create has its own local axes. These axes are (initially, at least)
aligned to the world axes. FIG-30.5 shows a cuboid and its local axes.
FIG-30.5 +y
Each Object has its
Own Local Axes
+z
-x
+x
-z
Each 3D object
has its own local
axes parallel to the
-y world axes
Rotation is performed in a clockwise direction when viewed down the positive end
of an axis (see FIG-30.7).
FIG-30.7 Rotation is in a
clockwise direction
Clockwise Rotation
3D Vectors
Although the purpose of this chapter is to describe basic 3D concepts, it's worth
mentioning that DarkBASIC Pro allows the creation of a 3-element vector
specifically for storing the coordinates of a point in 3D space. The vector is created
using the MAKE VECTOR3 statement which has the format shown in FIG-30.8.
FIG-30.8
The MAKE VECTOR3 MAKE VECTOR3 ( vectno )
Statement
integer
In the diagram:
The 3D vector
holds three values...
Many of the DarkBASIC Pro statements we'll encounter later make use of 3D
vectors for storing results, so it's useful to give you this quick grounding in them at
this early stage. We'll learn more on this subject in a later chapter.
Object Terminology
Just as 2D has a few basic shapes such as a line, a circle, a triangle and a rectangle,
so we have a set of basic shapes (known as primitives) in 3D. These include the
sphere, cylinder, cone, and cube. In FIG-30.10 we see and example of a cube.
FIG-30.10
A Cube - An Example of
a Primitive
solid wireframe
The cube is shown in two ways: solid, with shading caused by the light falling on
its surface, and wireframe showing how the cube is constructed.
Polygon is the term used for a many-sided enclosed area. The simplest polygon
(that is, the one with the least sides) is the triangle. The point where two lines of a
polygon meet is known as a vertex. A triangle has three vertices (see FIG-20.11).
Triangle
FIG-30.11
The Vertices of a
Triangle Vetrices
FIG-30.12
Edges
Edges
The greater the number of polygons used to create an object, the more detailed and
realistic it will appear (see FIG-30.13). But there is a price to pay for greater detail
Textures
In solid mode (as opposed to wireframe), a 3D object has a bland grey surface, but
we can use an image wrapped around that object to give it a greater reality. By
wrapping the image of riveted steel plate round a sphere, we can create the illusion
of a metal ball. Wrap an image of wooden planks round the same sphere and we
create a wooden ball (see FIG-30.14).
FIG-30.14
Adding Texture to a 3D
Object
PNG and TGA files are amongst those formats capable of embedding an alpha
channel within the image. An alpha channel affects how visible an image is and is
probably best explained with an analogy.
Imagine you've just painted an image on a piece of glass and that the light
illuminating the picture comes from behind the glass (see FIG-30.15) - like looking
If we were to paint the back of the glass black, no light would get through and we
wouldn't see the picture. If we used grey paint rather than black, then some light
would get through. If we painted a pattern on the back of the glass using a mixture
of black, dark grey, and light grey paint, the image would appear to have bright,
dull and black areas depending on the paint on the back of the image.
This is how the alpha channel of an image works. As well as the basic red, green
and blue elements (or channels) that go to make up the image, a fourth, alpha,
channel is added. This is just another layer to the image which can only be shaded
using greyscale colours (white through to black). Where black is used, the original
image is unseen; where white is used the image appears at normal brightness (this
is where the glass analogy falls down since it would be at its brightest with no paint
on the back of the glass). Shades of grey give varying degrees of image brightness.
FIG-30.16 shows original images, alpha channels, and the overall effects created.
FIG-30.16
Using an Alpha Channel
Cameras
The real world is a vast place, but with the help of television we can view any part
of it - all we need is a camera. What the TV camera broadcasts we see on our screens.
Move the camera and we see a different part of the world.
This is exactly how the 3D world we create within the computer works; what we
see on the computer screen is the output from a virtual camera. The camera can be
moved, just like a real camera, revealing different parts of our new 3D world. We
can zoom the camera in or out allowing us to enlarge a distant object or show
everything within a small space.
DarkBASIC Pro creates and positions a single camera automatically at the start of
every program that uses 3D objects. The exact position of the camera depends on
the positioning of the 3D objects, since the camera normally places itself in order
to see the objects that have been created. However, as the programmer, you can take
complete control of the camera and thereby determine just exactly what appears on
the screen.
Lights
We can even set up the lights we want to use to illuminate our new world - just like
placing lights on a movie set. By positioning various types of lights in just the correct
positions, we can create any type of atmosphere we want - from dark and mysterious
to bright and sunny. Like cameras in the 3D world, the lights are invisible, but the
effects they create are not!
FIG-30.17
Surface Normals
surface
normal
Normals are stored as mathematical expressions and are not part of the visible
structure of the model.
In principal every polygon can have two surface normals: one on the top side and
one on the bottom. However, often models only use a single normal - on the side
facing outwards.
When using surface normals to calculate how an object should be lit, we sometimes
get a rather faceted appearance, with an obvious jump in shading from one polygon
to the next (see FIG-30.18).
FIG-30.18
Visible Polygons
Vertex normal
Vertex normal Vertex normal
Vertex normal
Every vertex in a polygon has a When two or more polygons have common vertices, that vertex has a separate
vertex normal. vertex normal for each of the polygons that share the vertex.
These vertex normals are calculated from the values of the two edges which meet
at that vertex.
Using vertex normals creates a smoother lighting effect, but requires more
calculations. You can see the effect produced in FIG-30.20.
FIG-30.20
Polygon Smoothing
Activity 30.1
Load and run the program basic3D.exe. This will demonstrate some of the
basic concepts covered in this chapter.
(You can download this program, and all other files used in this text from
www.digital-skills.co.uk)
As we'll see in the chapters that follow, DarkBASIC Pro has literally hundreds of
commands designed to help us create a 3D world and manipulate the objects in that
world.
Summary
l The 3D world uses three axes: x, y and z.
l 3D space is split into eight octants by the X-Y, X-Z and Y-Z planes.
l Rotation is in a clockwise direction (as viewed from the positive end of the axis
of rotation).
l Increasing the number of polygons used in a scene increases the load on the
computer.
l When faced by a heavy load, the computer will output at a reduced frame rate.
l Some images can contain alpha channels which effect lightness when the image
is used to texture a surface.
l Virtual cameras determine which parts of the 3D world are shown on the screen.
l The effects of lights on a surface are calculated using surface normals or vertex
normals.
l Vertex normals may be used to create smoother shading effects, but at the cost
of more complex calculations.
Creating a Cube
The MAKE OBJECT CUBE Statement
To create a cube on the screen, we use the MAKE OBJECT CUBE statement. Like
sprites, every 3D object created must be given an identifying integer value (its ID).
No two 3D objects within a program can be assigned the same ID. The size of the
cube is also defined in this statement, which has the format shown in FIG-31.2.
FIG-31.2
The MAKE OBJECT
MAKE OBJECT CUBE objno , size
CUBE Statement
In the diagram:
size is a real value specifying the width, height and depth of the
cube. This value is given in world units.
This would create a cube (with ID 1) which is 10 units wide, by 10 units high, by
10 units deep. FIG-31.3 shows a screen shot of the resulting cube.
FIG-31.3
A Cube in DarkBASIC Pro
This may not look too impressive as a 3D object, but that’s because we’re looking
at the cube straight on and therefore can only see the front face of the object.
Activity 31.1
When any of the 3D primitives is first created, its centre is positioned at the origin.
FIG-31.4 shows a model of what has been created by the program in LISTING-31.1.
The 3 axes and parts of the XZ and YZ planes have been included to give a clearer
picture of how the cube is positioned.
FIG-31.4
How the Cube is y-axis
Positioned by the
Program
z-axis
s
10 unit
un
its 10
10 units
x-axis
In the diagram:
In the diagram:
The statement
would create a sphere with a diameter of 40 units and assign it the ID number 3.
However, the sphere produced is constructed from a relatively small number of
polygons and hence its curve is not particularly smooth. By using the rows and
columns values, we can control the number of polygons used to construct the sphere
and thereby produce a more realistic effect. For example, the line
would create a much smoother sphere. FIG-31.7 shows the difference between the
default sphere and the more detailed one.
FIG-31.7
Creating a Smoother
Sphere
However, there's a price to be paid for the more detailed sphere; the more polygons
we use when creating any 3D shape, the more work the processor/video card needs
to do and this reduces the frames per second that can be achieved.
Activity 31.2
In the diagram:
In the diagram:
For example, we could make a cone of height 10.1 units using the statement:
Activity 31.3
Modify the program again to show a cone of the same height as the cylinder.
For example, we could create a plane which is 1000 units wide by 500 high using
the line:
MAKE OBJECT PLAIN 6,1000.0,500.0
The centre of the plane will be located at the origin (see FIG-31.11).
z-axis
x-axis
, x2 , y2 , z2 , x3 , y3 , z3
In the diagram:
The line
The additional spacing
used within the MAKE OBJECT TRIANGLE 7, 2,0,3, 5,0,3, 3.5,6,7
instruction is used to
highlight the various
parameter groupings.
would create the triangle shown in FIG-31.13.
(3.5,6,7)
z-axis
(2,0,3)
(5,0,3)
x-axis
Notice that, unlike any of the other objects, a triangle can be placed anywhere in
3D space.
Positioning an Object
Other than the triangle, every object is created with it’s centre at the point (0,0,0).
However, once an object has been created, DarkBASIC Pro offers several ways of
moving an object to another position.
FIG-31.14
The POSITION POSITION OBJECT objno , x , y , z
OBJECT Statement
In the diagram:
For example, if we wanted the centre of the cube we had created previously to be
moved to position (9,0,0), then we would use the statement:
z-axis
Activity 31.4
Notice that, in its final position, the cube looks smaller since it has now moved
further away from our viewing position.
There are four possible directions available: RIGHT, LEFT, UP and DOWN. The
statement has the format shown in FIG-31.16.
FIG-31.16
{}
The MOVE OBJECT UP
Statement
DOWN
MOVE OBJECT objno , dist
LEFT
RIGHT
In the diagram:
FIG-31.17
Using the MOVE
OBJECT Statement MOVE OBJECT UP
Notice that there is no option to move the object backwards or forwards (i.e. along
the z-axis). For example, object 1 could be moved 10.5 units to the right using the
statement:
MOVE OBJECT RIGHT 1,10.5
Activity 31.6
FIG-31.18
y-axis
y-axis
An Object's Local Axes
z-axis z-axis
x-axis x-axis
local y-axis
For example, a cube (object 1) can be rotated about the x-axis to 45o using the line
FIG-31.21
The Effect of the
XROTATE OBJECT
Statement
o o
Cube 0 rotation Cube at 45 rotation
Activity 31.7
Wait 1 millisecond
ENDFOR
Modify the program so that the cube revolves in the opposite direction about
the x-axis.
In the diagram:
angle is a real number giving the angle (in degrees) to which the
object is to be rotated.
For example, a cube (ID 1) could be rotated about its y-axis to 60o using the line:
YROTATE OBJECT 1, 60.0
FIG-31.23
The Effect of the
YROTATE OBJECT
Statement
In the diagram:
angle is a real number giving the angle (in degrees) to which the
object is to be rotated.
For example, a cube (ID 1) could be rotated about the z-axis to 110o using the line:
FIG-31.25
The Effect of the
ZROTATE OBJECT
Statement
Activity 31.8
In the diagram:
xangle is a real number giving the angle (in degrees) to which the
object is to be rotated about its x-axis.
yangle is a real number giving the angle (in degrees) to which the
object is to be rotated about its y-axis.
zangle is a real number giving the angle (in degrees) to which the
object is to be rotated about its z-axis.
Activity 31.9
Rewrite the program you created in the previous Activity, replacing the
XROTATE, YROTATE and ZROTATE statements with a single ROTATE
OBJECT statement, producing the same effect as before.
All statements in the previous section rotate an object to a specific angle, irrespective
FIG-31.27
The SET OBJECT
ROTATION ZYX
Statement
SET OBJECT ROTATION
{ { XYZ
ZYX
objno
In the diagram:
When using relative rotation, different terms are used for rotation about each axis.
Hence, we use the term PITCH for rotation about the x-axis, TURN for rotation
about the y-axis and ROLL for rotation about the z-axis (see FIG-31.28).
FIG-31.28
DOWN local z-axis
Relative Rotation Terms
RIGHT LEFT
RIGHT LEFT
local x-axis
UP
local y-axis
UP
objno , angle
In the diagram:
angle is a real number giving the angle (in degrees) to which the
object is to be rotated relative to its current position.
The angle can be a positive or negative value.
The program in LISTING-31.4 performs the same function as the one you created
in Activity 31.7 where you used the XROTATE statement to rotate a cube through
360o. However, this time the XROTATE statement has been replaced by a PITCH
OBJECT UP command.
LISTING-31.4
REM *** Set screen resolution ***
Using Relative Rotation SET DISPLAY MODE 1280,1024,32
REM *** Create and position cube ***
MAKE OBJECT CUBE 1, 40
POSITION OBJECT 1, 0, 0, 200
Activity 31.10
Modify the program so that the cube rotates in the opposite direction.
{ {
FIG-31.30
LEFT
The TURN OBJECT TURN OBJECT objno , angle
Statement RIGHT
Activity 31.11
Modify your previous program so that the cube rotates to the right about the
y-axis.
RIGHT
objno , angle
In the diagram:
Activity 31.12
Modify your previous program so that the cube rotates to the left about the
z-axis.
In the program shown in LISTING-31.5 a cube is made to face the point (45,45,0)
using the statement:
LISTING-31.5
REM *** Set display resolution and backdrop ***
SET DISPLAY MODE 1280,1024,32
Using the POINT
COLOR BACKDROP 0
OBJECT Statement
BACKDROP ON
REM *** Make the set of objects ***
MAKE OBJECT CUBE 1,40
REM *** Move cube to (0,0,100) after key press ***
WAIT KEY
POSITION OBJECT 1,0,0,100
REM *** point cube at (45,45,0)***
WAIT KEY
POINT OBJECT 1,45,45,0
REM *** End program ***
WAIT KEY
END
Initially, the cube’s main polygon faces the After the POINT OBJECT statement is
viewer. executed, the main polygon faces (45,45,0).
Activity 31.13
Modify the program to make the cube face the point (-20,17,-10).
Activity 31.14
local x-axis
local y-axis
local y-axis
o
Local Axes - Initial Position Local Axes - Cube Rotated to -90
about the z-axis
If we now rotate the cube about its own x-axis, it will turn left-to-right rather than
up-and-over, because its x-axis has shifted position. This is demonstrated in
LISTING-31.6 where the cube is rotated a full 360o about its x-axis, rotated by -90o
about its z-axis and then rotated a full 360o about its x-axis for a second time.
LISTING-31.6 REM *** Set display resolution ***
SET DISPLAY MODE 1280,1024,32
Local Axes Move with
REM *** Make and position cube ***
the Object
MAKE OBJECT CUBE 1,40
POSITION OBJECT 1,0,0,100
Activity 31.15
When an object has been rotated, it is possible to reset the local axes so that they
are parallel to the main axes. This is done using the FIX OBJECT PIVOT statement
whose format is shown in FIG-31.36.
FIG-31.36
The FIX OBJECT PIVOT FIX OBJECT PIVOT objno
Statement
In the diagram:
When this statement is executed, the object in question has its local axes reset so
that the x-axis lies left-to-right, the y-axis top-to-bottom, and the z-axis in-to-out
(see FIG-31.37).
local x-axis
FIG-31.37 local z-axis
local z-axis
local x-axis
local y-axis
local y-axis
local y-axis
local z-axis
local x-axis
Modify the program again so that the cube is only rotated to -45o in the second
FOR loop.
Resizing Objects
It is possible to change the size of an object after it has been created. You have the
option to resize one, two, or all three of the object’s dimensions. This allows you
to make an object uniformly larger or smaller, or to distort the original shape by
changing each dimension by differing amounts.
The program in LISTING-31.7 creates a sphere with a radius of 20 units. The sphere
is then resized so that the x dimension is doubled and the z dimension reduced to
10 units. The new shape is then rotated about the y-axis. The user must press ESC
to terminate the program.
Activity 31.17
In the diagram:
Activity 31.18
Copying a 3D Object
We can create a copy of an existing 3D object in one of two ways, as described
below.
In the diagram:
Activity 31.19
Although this may seem to have the same affect as the CLONE OBJECT statement,
in fact the two statements differ in how data about the copied object is held. When
CLONE OBJECT is used, the new object has its own independent data area; with
INSTANCE OBJECT the two objects share parts of the same data area. The
consequence of this is that objects created using INSTANCE OBJECT will
disappear if the original object from which they were created is deleted.
Activity 31.20
Modify your last program, replacing the CLONE OBJECT statement with a
INSTANCE OBJECT statement.
Change the DELETE OBJECT statement so that object 2, rather than object 1,
is deleted. How does this affect the program?
integer
In the diagram:
{
The OBJECT POSITION X
Statement
OBJECT POSITION Y ( objno )
real
In the diagram:
For example, we could determine the position in space of object 1's centre using the
lines:
FIG-31.47
OBJECT VISIBLE ( objno )
The OBJECT VISIBLE
Statement
integer
In the diagram:
FIG-31.48
{
X
The OBJECT SIZE
Statement
OBJECT SIZE Y ( objno )
real
In the diagram:
The value returned by the statement is real and, because of rounding errors, this may
be slightly out. For example, if we create a cube (object 1) 40 units in all directions,
then the statement
PRINT "Width ", OBJECT SIZE X(1)
Also, the OBJECT SIZE (1) statement - with no reference to any specific dimension
- gives an overall size based on all three dimensions.
Activity 31.21
real
In the diagram:
Activity 31.22
Modify your previous program so that the box object is rotated by a random
number of degrees about all three axes. Display the amount of rotation in each
case.
Before looking at the code, we have one main obstacle to overcome. The mouse
pointer commands (MOUSE X() and MOUSE Y() ) use a 2D coordinate system
with the origin at the top left corner of the screen; 3D objects use a coordinate system
in which the origin is (initially, at least) at the centre of the screen. To convert the
mouse's x ordinate readings to 3D space we need to use the line:
The y ordinate also needs to have it's sign changed, since for the mouse the positive
section of the y-axis is down, while in 3D space the positive section of the y-axis
is up! We can solve this with the line:
Create cube
Move cube backwards to reduce its apparent size
DO
Activity 31.23
Modify the program to use two spheres, set side-by-side, both of which should
face towards the mouse pointer.
In the diagram:
mode is 0 or 1.
0 - solid mode
1 - wireframe mode
Activity 31.24
In the diagram:
mode is 0 or 1.
0 - culling off
1 - culling on
Activity 31.25
immediately before the DO..LOOP structure. Notice that the hidden polygons
are now being drawn.
Modify the program again so that pressing the w key toggles between
wireframe and solid mode and that pressing c toggles between culling on and
culling off.
There's a slight problem when it comes to displaying cylinders and cones, as we can
see from the output produced by LISTING-31.12.
Activity 31.26
The inside surfaces of both shapes have not been drawn (see FIG-31.52).
FIG-31.52
Hidden Surfaces on
Cones and Cylinders
But we can solve this problem by switching off culling, so that the hidden polygons
are drawn.
Activity 31.27
Modify your previous program so that culling is switched off for both the cone
and the cylinder.
Storage Methods
When a 3D object is shown on screen, the coordinates of its vertices are stored in
memory in an area known as a vertex buffer. Normally, each object will have its
own vertex buffer. However, some video cards allow different objects to share
vertex buffers, other video cards don't. As a default, 3D objects in DARKBASIC
Pro do not share vertex buffers - this ensures compatibility with the maximum
number of video cards. However, it is possible to force vertex buffer sharing and,
if your video card can handle this, improve the performance of your program.
Even if your own video card does allow vertex buffer sharing, your customer's may
not, so it's probably best to ignore this option.
Summary
l A point in 3D space is specified using x, y, and z coordinates.
l There are three main planes in 3D space - XY, YZ, and XZ.
l The positive z-axis travels away from the viewer "into" the screen.
l Every 3D object has its own local axes with the origin at the centre of the object.
l Use ROTATE OBJECT to rotate an object to specific angles about all three local
axes at the same time.
l Use MOVE OBJECT distance to move the object a specified number of units in
the direction in which an object is pointing.
l Use FIX OBJECT PIVOT to reset an object's local axes to be in line with the
global axes.
Create the new shape from the combination of the two original shapes - known as
union (see FIG-31.54).
FIG-31.54
Shape Union
union
Create the new shape by removing the overlapping section of shape 2 from shape
1 - known as difference (see FIG-31.55).
FIG-31.55
Shape Difference
difference
Create the new shape from the overlapping area between shape 1 and shape 2 -
known as intersection (see FIG-56).
FIG-31.56
Shape Intersection
intersection
The Statements
When we join two shapes using the merge statements, the resulting shape is stored
in a format knows as Constructive Solid Geometry (CSG). We need not concern
ourselves with the details of this format, and it is only mentioned here so that you
know the meaning of the initials used in the statements.
In the diagram:
The statement modifies the shape of objno1 without affecting objno2. Normally,
the programmer would delete the second object once the union is completed.
The program in LISTING-31.13 demonstrates the union of a cube and a box. After
the box has been deleted, the resulting shape is then rotated about its local x and y
axes.
Activity 31.28
Try changing the second object to a sphere of diameter 10 and change the
POSITION OBJECT statement to read
In the diagram:
As before, the second object is unaffected by the operation and would normally be
deleted. Also, we are again restricted to cubes and boxes if we are to obtain
consistent results.
Activity 31.29
Restore your last project to its original code (as shown in LISTING-31.13).
Change the union operation to a difference operation and observe the new
shape created.
FIG-31.59
The PERFORM CSG
PERFORM CSG INTERSECTION objno1 , objno2
INTERSECTION
Statement
In the diagram:
Again, the second object would normally be deleted and we are restricted to cubes
and boxes if we are to obtain predictable results.
Modify your previous program and determine how the shape created by the
PERFORM CSG INTERSECTION differs from that produced by PERFORM
CSG DIFFERENCE.
Summary
l Cubes and boxes can be merged to create new shapes.
1.
REM *** Set display and backdrop *** Activity 31.6
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0 REM *** Set display & backdrop ***
BACKDROP ON SET DISPLAY MODE 1280,1024,32
REM *** Make the cylinder *** COLOR BACKDROP 0
MAKE OBJECT CYLINDER 1,5 BACKDROP ON
REM *** End program *** REM *** Make the set of objects ***
WAIT KEY MAKE OBJECT CUBE 1,10
END REM *** Cube to(9,0,100) after key
press ***
2. WAIT KEY
REM *** Set display and backdrop *** POSITION OBJECT 1,9,0,100
SET DISPLAY MODE 1280,1024,32 REM *** Cube 31.3 units to the right
COLOR BACKDROP 0 ***
BACKDROP ON WAIT KEY
REM *** Make the cone *** MOVE OBJECT RIGHT 1, 31.3
MAKE OBJECT CONE 1,5 REM *** End program ***
REM *** End program *** WAIT KEY
WAIT KEY END
END
Activity 31.7
Activity 31.4
Version 1
REM *** Set display & backdrop *** REM *** Set display & backdrop
SET DISPLAY MODE 1280,1024,32 SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0 COLOR BACKDROP 0
BACKDROP ON BACKDROP ON
REM *** Make the cube *** REM *** Make the set of objects ***
MAKE OBJECT CUBE 1, 10 MAKE OBJECT CUBE 1,40
REM *** Cube to (50,0,0) after key REM ***Cube to(0,0,100) after key
press *** press ***
WAIT KEY POSITION OBJECT 1,0,0,100
POSITION OBJECT 1,9,0,0 REM *** Rotate cube ***
REM *** Move the cube backwards *** FOR degree = 1 TO 360
WAIT KEY XROTATE OBJECT 1, degree
POSITION OBJECT 1, 9,0,30 WAIT 1
REM *** End program *** NEXT degree
WAIT KEY REM *** End program ***
END WAIT KEY
END
Activity 31.21
Activity 31.24
REM *** Set display resolution ***
SET DISPLAY MODE 1280,1024,32 No solution required.
REM *** Seed random number generator ***
RANDOMIZE TIMER()
REM *** Make and position the box *** Activity 31.25
MAKE OBJECT BOX 1, RND(45)+5,RND(45)+5,
ÄRND(45)+5 REM *** Set screen mode ***
POSITION OBJECT 1, 0,0,0 SET DISPLAY MODE 1280,1024,32
REM *** Get dimensions of box *** REM *** Make and position cube ***
width# = OBJECT SIZE X(1) MAKE OBJECT CUBE 1, 40
height# = OBJECT SIZE Y(1) POSITION OBJECT 1,25,0,100
depth# = OBJECT SIZE Z(1) REM *** Start in solid and culling on ***
REM *** Display dimensions *** wire = 0
DO cull = 1
SET CURSOR 10,20 REM *** Rotate cube ***
PRINT "Width : ",width#, " Height :", DO
Ä height#, " Depth : ",depth# REM *** IF key pressed, switch mode ***
LOOP IF INKEY$() ="w"
REM *** End program *** wire = 1 - wire
WAIT KEY SET OBJECT WIREFRAME 1, wire
END ENDIF
IF INKEY$()="c"
cull = 1 - cull
Activity 31.22 SET OBJECT CULL 1,cull
ENDIF
REM *** Set display resolution *** PITCH OBJECT DOWN 1, 1.0
SET DISPLAY MODE 1280,1024,32 TURN OBJECT LEFT 1, 1.0
REM *** Seed random number generator *** LOOP
RANDOMIZE TIMER() REM *** End program ***
REM *** Make and position the box *** END
MAKE OBJECT BOX 1, RND(45)+5,RND(45)+5,
ÄRND(45)+5
POSITION OBJECT 1, 0,0,0 Activity 31.26
REM *** Rotate box at random ***
ROTATE OBJECT 1,RND(359),RND(359),RND(359) The inside surfaces of both shapes are not drawn.
REM *** Get dimensions of box ***
width# = OBJECT SIZE X(1)
height# = OBJECT SIZE Y(1) Activity 31.27
depth# = OBJECT SIZE Z(1)
REM *** Get rotations of box *** REM *** Set screen mode ***
x_axis_rotation = OBJECT ANGLE X(1) SET DISPLAY MODE 1280,1024,32
y_axis_rotation = OBJECT ANGLE Y(1)
z_axis_rotation = OBJECT ANGLE Z(1) REM *** Make & position cone and cylinder ***
REM *** Display details *** MAKE OBJECT CONE 1, 5
DO POSITION OBJECT 1,-6,-5,0
SET CURSOR 10,20 XROTATE OBJECT 1, 45
PRINT "Width : ",width#, " Height : " MAKE OBJECT CYLINDER 2,5
Ä, height#, " Depth : ",depth# POSITION OBJECT 2, 6,-5,0
SET CURSOR 10,40
PRINT "X-axis : ",x_axis_rotation, REM ** Culling off for both objects ***
Ä" y-axis : ",y_axis_rotation, SET OBJECT CULL 1,0
Ä" z-axis : ", z_axis_rotation SET OBJECT CULL 2,0
LOOP
REM *** End program *** REM *** End program ***
WAIT KEY WAIT KEY
END END
Activity 31.30
REM *** Set screen resolution and background ***
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON
REM *** Position camera ***
AUTOCAM OFF
POSITION CAMERA 0,0,-100
REM *** Create two shapes used ***
MAKE OBJECT CUBE 1,40
MAKE OBJECT BOX 2,10,30,10
POSITION OBJECT 2,0,15,0
REM *** Let viewer see position of shapes ***
WAIT KEY
REM *** Intersection shapes ***
PERFORM CSG INTERSETION 1,2
REM *** Remove object 2 ***
DELETE OBJECT 2
REM *** Rotate new shape ***
DO
TURN OBJECT LEFT 1,1.0
PITCH OBJECT UP 1,1.0
LOOP
REM *** End program ***
END
Colouring a 3D Object
Mipmaps
Offsetting a Texture
Overlaying Textures
Seamless Tiling
Semi-Transparent 3D Object
Sky Spheres
Texture Mapping Options
Texture Transparency
Tiling
Video Texturing
After this has been done, we can transfer the image to the surface of one or more
3D objects.
Activity 32.1
We can see quite clearly from the results of the last Activity that the image is applied
separately to each face of the cube. For other shapes, the image may be applied
differently.
Activity 32.2
Mipmaps
Texturing a 3D object can be quite time consuming. It may be easy enough to map
a 300 by 300 pixel image onto a flat surface which occupies exactly 300 by 300
pixels on the screen, but if the 3D object moves off into the distance, the computer
has to work much harder to map the same 300 by 300 image onto an object which
now occupies just 23 by 23 pixels on the screen.
To help with this problem DarkBASIC Pro creates more than one copy of any image
that is loaded, with each copy being exactly half the size of the last (see FIG-32.3).
As a textured 3D object becomes smaller on the screen, the version of the image
used to texture that object changes from the the largest to the smallest.
Activity 32.3
The effect is a fairly subtle one. Look closely at the image on the cube as it
moves away from your viewpoint. You should see it become less distinct as it
gets smaller.
In the diagram:
When an image is loaded without mipmaps, any object using that image as a texture
must continue to use the original image even when the 3D object is greatly reduced
in size on the screen.
Activity 32.4
Modify your last program so that no mipmaps are used. To do this change the
LOAD IMAGE line to read:
How does the texture on the cube differ in this program from the earlier
version?
Tiling
In the next example we'll create the floor of a dungeon by texturing a plane using
a cobblestone image.
Activity 32.5
FIG-32.5
Floor Texturing
One way to solve the problem would be to use an image which actually shows the
hundreds of blocks that we need to create a realistic floor. However, this may not
be possible and the image would certainly have to be large if the visuals are to look
convincing as a character moves over the floor.
A second option is to make the texture image repeat itself several times over the
surface of the plane. This, for rather obvious reasons, is known as tiling.
The UV Coordinate
System used by a Texture
Image
(0,1) (1,1)
When mapped to a flat plane, the image spreads itself over the object with point
(0,0) of the image mapping to the top-left corner of the plane and point (1,1) to the
bottom-right corner (see FIG-32.7).
(1,1)
Plane
FIG-32.8
The Effects of Using
SCALE OBJECT
TEXTURE Part of the Image Used The Image Duplicated to Create
as Texture a Tiled Effect
The SCALE OBJECT TEXTURE statement has the format shown in FIG-32.9.
FIG-32.9
The SCALE OBJECT SCALE OBJECT TEXTURE objno , Uscale , Vscale
TEXTURE Statement
In the diagram:
The first example shown in FIG-32.8 was created using the line:
Modify the scaling factors to each of the following settings and observe the
results:
Uscale Vscale
5.0 5.0
2.0 2.0
0.5 0.5
5.0 1.0
1.0 5.0
Change the 3D object used in your program from a plane to a sphere and retry
each of the settings given above.
Scaling a texture image in this way affects the image itself, so there is no way to
return to the original image settings within a program.
Seamless Tiling
For tiling to be convincing, the ends of the repeating image must butt together
without too obvious a join.
If we start with a simple picture and use it as a tiled texture (as shown in FIG-32.10)
we get a disappointing effect in which the edge of each image tile is very obvious.
FIG-32.10
A Visible Join Between
Tiles
To avoid this, we need to modify the image using a paint package such as Paint
Shop Pro or Photoshop.
Load the original image into the Select a section of the image Copy selected area, mirror it,
paint program. along the whole of the left edge. and move it to the right edge.
Of course, it is equally
possible to copy the
right edge area to the left
side and the top to the
bottom.
It takes a bit of practice to achieve good results when creating a texture image, but
the results can be worth it.
Even when an image is not tiled, we can still have a problem with seams. For
example, when the image eyecol.bmp is applied as a texture to a sphere, the join
between the left and right edges of the image is quite apparent at the back of the
sphere, while the top and bottom edges are squeezed into single points at the two
"poles".
Activity 32.7
Write a short program (act3207.dbpro) which applies the new file to a sphere
and then rotates the sphere continuously about its local y-axis.
Video Texture
It is even possible to use a video clip as a surface texture. To do this we need to start
by loading up a video with an instruction such as:
In the diagram:
The size of the play area (as set by x1, y1, x2, y2) affects the quality of the image
when it appears on the 3D object; use too small a set of values and the video will
be heavily pixellated; use too large a set of values and displaying the video will put
too great a load on the processor/video card and slow the whole thing down.
Once the video has been transferred to the image object, we can then use the image
to texture a 3D object in the usual manner. LISTING-32.5 demonstrates the effect
by placing a video on a rotating cube.
Activity 32.8
Modify the program to use very low values for the bottom right corner of the
video (i.e. 10,10) and very high values (i.e. 1000,1000). What affect do these
changes have on the final result?
In the diagram:
In FIG-32.14 we see the effects of each possible value for tmode when applying
eyecol.bmp as a tiled (2 by 2) texture on a cube.
FIG-32.14
The Effects of Using
Different tmode Settings
set object texture 1,1,0 set object texture 1,2,0 set object texture 1,3,0 set object texture 1,4,0
The program in LISTING-32.6 shows a tile textured cube with the tmode value of
the SET OBJECT TEXTURE statement set to 2.
Activity 32.9
Try other settings for tmode and check the effects produced.
The effect is created using the SCROLL OBJECT TEXTURE statement which has
the format shown in FIG-32.15.
FIG-32.15
The SCROLL OBJECT SCROLL OBJECT TEXTURE objno , Uoffset , Voffset
TEXTURE Statement
In the diagram:
FIG-32.16
The Effects of the
SCROLL OBJECT
TEXTURE Statement
scroll object texture 1,0.1,0.0 scroll object texture 1,0.0,0.1 scroll object texture 1,0.1,0.1
Activity 32.10
The effect makes a permanent change to the texture for that image, so repeating the
same statement creates a further offset.
Activity 32.11
By placing the SCROLL OBJECT TEXTURE statement in a loop, the texture can
scroll over the surface of the 3D object.
Activity 32.12
Remove the second SCROLL OBJECT TEXTURE statement from your last
program and insert the remaining SCROLL OBJECT TEXTURE statement in
a DO..LOOP structure.
Activity 32.13
Modify that program so that the spheres are textured using seamlesseye.bmp.
A colour other than black When an image containing black is mapped to a sprite, any black areas in the image
can become the are automatically transparent when the sprite appears on the screen. However, this
transparent colour using is not the case with 3D objects.
the SET COLOR KEY
statement.
To demonstrate this, the next program (see LISTING-32.8) uses the image shown
in FIG-32.17 as the texture on a rotating cube.
FIG-32.17
Image used to Texture a
Cube
Activity 32.14
However, we can force a 3D object to make black (or whatever other colour as been
set as the background colour using the SET COLOR KEY statement) transparent
using the SET OBJECT TRANSPARENCY statement which has the format shown
in FIG-32.18.
FIG-32.18
The SET OBJECT
SET OBJECT TRANSPARENCY objno , transflag
TRANSPARENCY
In the diagram:
Activity 32.15
For example, if we take the images shown in FIG-32.19 with image 1 being the
basic texture and image 2 the overlaid texture, then we achieve the effect shown in
FIG-32.20 when these are applied to a cube.
FIG-32.19
The Images Used To
Texture a 3D Object
Image 1 Image 2
FIG-32.20
The Two Images Applied
to a Cube
A second image is applied to the texture of an object using the SET DETAIL
MAPPING ON statement which has the format shown in FIG-32.21.
FIG-32.21
The SET DETAIL SET DETAIL MAPPING ON objno , imgno
MAPPING ON Statement
In the diagram:
LISTING-32.9 creates the rotating cube shown above. The main lines of code are
LOAD IMAGE "DoNot.bmp",2
Activity 32.16
We are limited to a single image when overlaying an object's texture with detail.
So, attempting to add a second detail image will simply remove the first from the
object.
Activity 32.17
Add FlagMag.bmp as a second detail image to the cube object in your last
program.
If an object's texture has been tiled using the SCALE OBJECT TEXTURE
statement, any additional image added using SET DETAIL MAPPING ON will
also be tiled to the same extent as the original texture.
In your last program, remove all references to the flagmag.bmp file. Create a
tiled effect on the cube by using the SCALE OBJECT TEXTURE statement
with the Uscale and Vscale parameters both set to 2.
How is the DETAIL MAPPING image on the cube affected by the tiling?
In the diagram:
filterflag is 0, 1, or 2
0 - always uses the original image
to texture (it never uses the smaller
images created by mipmapping).
1 - no smoothing is used.
2 - uses linear filtering.
The program in LISTING-32.10 creates 3 spheres, each textured using one of the
filter options. You may see a slight difference in the appearance of the spheres as
they move off into the background and reduce in size.
LISTING-32.10 REM *** Set screen resolution ***
SET DISPLAY MODE 1280,1024,32
Using the SET OBJECT
REM *** Load image used as texture ***
FILTER Statement
LOAD IMAGE "grid8by8.bmp",1,1
REM *** Create three spheres ***
MAKE OBJECT SPHERE 1 ,40
MAKE OBJECT SPHERE 2 ,40
MAKE OBJECT SPHERE 3 ,40
Activity 32.19
Summary
l Texturing involves mapping an image onto the surface of a 3D object.
l Use LOAD IMAGE to load any image which is to be used to texture a 3D object.
l Mipmaps are smaller versions of the original image which are used to speed up
mapping when a textured object becomes much smaller than the original image.
l To create a seamless tile, make sure the opposite edges are complementary.
l Use SET OBJECT FILTER to modify how an image is filtered when being
mapped onto an object.
FIG-32.23
The COLOR OBJECT COLOR OBJECT objno , colour
Statement
In the diagram:
For example, we could give object 1 a red surface using the line:
COLOR OBJECT 1, RGB(255,0,0)
A coloured surface cannot be used in conjunction with a main texture, but secondary
textures (created using SET DETAIL MAPPING ON) may still be used.
Activity 32.21
We can create this effect using the GHOST OBJECT ON statement which has the
format shown in FIG-32.25.
FIG-32.25
The GHOST OBJECT
GHOST OBJECT ON objno , ghostflag
ON Statement
In the diagram:
ghostflag is 0 to 5.
0 - object is semi-transparent
1 - object uses negative transparency
2 - object is semi-transparent but lighter
3 - uses the image's alpha channel
4 - similar to 1 but lighter
5 - object is opaque
Activity 32.22
In the diagram:
In the diagram:
Activity 32.23
When used in combination with the GHOST OBJECT ON statement, the FADE
OBJECT statement can make a semi-transparent object disappear completely.
There's still more to be said about texturing objects but we'll leave that to a later
chapter after we've covered other basic concepts such as cameras and lighting.
Summary
l Use COLOR OBJECT to tint the surface of a 3D object.
l When used on a transparent object, FADE OBJECT can make that object
invisible.
l When used on an opaque object, FADE OBJECT can make that object
completely black.
We've already seen that the SET OBJECT TRANSPARENCY statement affects
the black area of an texture image, but the statement also controls how an alpha
channel within an image affects the final texture.
Activity 32.25
An image with an alpha channel also produces an effect when option 3 is used with
the GHOST OBJECT ON statement.
Activity 32.26
to
Summary
l Use SET OBJECT TRANSPARENCY with an alpha channel image to create
transparent or semitransparent texturing effects.
l Use GHOST OBJECT ON with option 3 to make use of the alpha channel
information in creating the final ghosting effect.
FIG-32.29
A Rough Sketch of the
Object Required
Splitting the code in this way will help us keep the structure as understandable as
possible.
FUNCTION DrawCastle()
LoadImages()
DrawGrounds()
DrawExternalWalls()
DrawRoofandCeiling()
DrawTurrets()
DrawInternalColumns()
ENDFUNCTION
FUNCTION LoadImages()
REM *** Load texture images ***
LOAD IMAGE "grass1.jpg",lawn
LOAD IMAGE "CobbleStones.jpg",road
LOAD IMAGE "tree.jpg",tree
LOAD IMAGE "rock2.jpg",flooring
LOAD IMAGE "stone.jpg",wall
LOAD IMAGE "tiles.jpg",roofing
LOAD IMAGE "ceil_U3_01.jpg",cover
ENDFUNCTION
FUNCTION DrawExternalWalls()
REM *** Create front wall ***
MAKE OBJECT PLAIN frontwall,550,100
TEXTURE OBJECT frontwall,wall
SCALE OBJECT TEXTURE frontwall,30,6
POSITION OBJECT frontwall, 225,50,700
FUNCTION DrawTurrets()
REM *** Create first turret ***
MAKE OBJECT CYLINDER turret1,200
SCALE OBJECT turret1,40,100,40
MAKE OBJECT CONE turretroof1,81
REM *** Texture turret ***
TEXTURE OBJECT turret1,wall
SCALE OBJECT TEXTURE turret1,10,10
TEXTURE OBJECT turretroof1,roofing
SCALE OBJECT TEXTURE turretroof1,5,10
REM *** Position turret ***
POSITION OBJECT turret1, -25,100,970
POSITION OBJECT turretroof1,-25,240,970
FUNCTION DrawRoofAndCeiling()
REM *** Create main roof ***
MAKE OBJECT PLAIN roof1,550,300
REM *** Texture main roof ***
TEXTURE OBJECT roof1,roofing
SCALE OBJECT TEXTURE roof1,50,10
REM *** Position roof ***
XROTATE OBJECT roof1, -90
POSITION OBJECT roof1,225,100,850
REM *** Create ceiling ***
MAKE OBJECT PLAIN ceiling,550,300
REM *** Texture ceiling ***
TEXTURE OBJECT ceiling,cover
SCALE OBJECT TEXTURE ceiling,5,2
REM *** Position ceiling ***
XROTATE OBJECT ceiling, -90
POSITION OBJECT ceiling,225,99,850
ENDFUNCTION
FUNCTION DrawInternalColumns()
RANDOMIZE TIMER()
FOR col = column TO column + 80
MAKE OBJECT BOX col,20,99.8,20
TEXTURE OBJECT col,wall
SCALE OBJECT TEXTURE col,5,30
POSITION OBJECT col,RND(515)-20,49.95, RND(270)+710
NEXT col
ENDFUNCTION
continued on next page
The DrawTrees() function draws a set of trees by texturing a set of planes with a
tree image.
Activity 32.27
Activity 32.28
Remove the main section from the program, leaving only the constants and
functions. Save this as castle.dbpro.
Activity 32.29
after the call to the DrawGallows() function. This will ensure that the camera
is pointing at the gallows.
To implement a sky sphere in our gallows program, we'll start by making the ground
plane set up in DrawGallows() a bit larger
and then increase the tiling so the cobbles don't get too large:
These are the only changes required in the DrawGallows() function. Now we can
add a few lines to the main section. First we need the image to be used to texture
the sphere with a sky effect:
LOAD IMAGE "sky.jpg",1
Next we can create the sphere with the same diameter as the plane:
Notice that the sphere has been created with extra polygons. This helps smooth out
the sky background.
Modify your gallows.dbpro program using the lines given above. The main
section should be coded as:
DrawGallows()
We can't see the texture on the sphere because we're on the inside of the sphere and
DarkBASIC Pro has culled the polygons that make up the sphere.
There are two ways to solve this problem. The first is to switch off culling on the
sphere. This can be done using the line:
Activity 32.31
Add the above line to the main section of your code. Is the sky now visible?
An alternative way of displaying the sphere's texture is to turn the sphere inside out!
This is done by specifying a negative size for the sphere when it is being created.
Activity 32.32
Activity 32.33
If you have an appropriate paint package, invert and mirror the image sky.jpg
(save the resulting image as skyIM.jpg) and use the new image as a texture for
the sky sphere.
If you don't have an appropriate package, the inverted mirror image is supplied
with the images for this chapter.
Summary
l A sky sphere allows us to create a sky affect around our 3D world.
l To make the sphere's texture visible from within the sphere, switch off the
sphere's culling or create and inverted mirror image of the sky and use it to texture
a sphere with a negative diameter value.
Activity 32.5
Activity 32.12
No solution required.
REM *** Set display resolution ***
SET DISPLAY MODE 1280,1024,32
Activity 32.6 REM *** Load image ***
LOAD IMAGE "eyecol.bmp",1
No solution required. REM *** Create and texture plain object ***
MAKE OBJECT PLAIN 1,200,200
TEXTURE OBJECT 1,1
REM *** Offset texture placed on image***
Activity 32.7 DO
REM *** Set display resolution *** SCROLL OBJECT TEXTURE 1,0.1,0.0
SET DISPLAY MODE 1280,1024,32 LOOP
REM *** Load texture image *** REM *** End program ***
LOAD IMAGE "seamlesseye.bmp",1 WAIT KEY
REM *** Create and texture sphere *** END
MAKE OBJECT SPHERE 1, 40,40,40
TEXTURE OBJECT 1,1 The image scrolls vertically.
DO
TURN OBJECT LEFT 1, 1.0 REM *** Set display resolution ***
LOOP SET DISPLAY MODE 1280,1024,32
END REM *** Load image ***
LOAD IMAGE "eyecol.bmp",1
Only the wood and flag textures show; the text "DO NOT
Activity 32.15 OPEN" is missing.
REM *** Set display resolution ***
SET DISPLAY MODE 1280,1024,32
REM *** Load texture image *** Activity 32.18
LOAD IMAGE "DoNot.bmp",2 REM *** Set display resolution ***
REM *** Make and position cube *** SET DISPLAY MODE 1280,1024,32
MAKE OBJECT CUBE 1, 40 REM *** Set magenta as transparent ***
POSITION OBJECT 1,25,0,100 SET IMAGE COLORKEY 255,0,255
REM *** Texture cube with image *** REM *** Load texture images ***
TEXTURE OBJECT 1,2 LOAD IMAGE "textureWood.jpg",1
REM *** Make black areas of texture *** LOAD IMAGE "DoNotMag.bmp",2
REM *** transparent *** REM *** Create and texture cube ***
SET OBJECT TRANSPARENCY 1,1 MAKE OBJECT CUBE 1, 40
REM *** Rotate cube *** TEXTURE OBJECT 1,1
DO REM *** Tile cube's texture ***
PITCH OBJECT DOWN 1, 1.0 SCALE OBJECT TEXTURE 1,2,2
TURN OBJECT LEFT 1, 1.0 REM *** Add secondary texture ***
LOOP SET DETAIL MAPPING ON 1,2
REM *** End program *** REM *** Position cube ***
END POSITION OBJECT 1,25,0,100
REM *** Rotate cube ***
Any part of the cube which is textured with black DO PITCH OBJECT DOWN 1, 1.0
disappears. TURN OBJECT LEFT 1, 1.0
LOOP
REM *** End program ***
Activity 32.16 END
REM *** Set display resolution *** The DETAIL MAPPING image is also tiled.
SET DISPLAY MODE 1280,1024,32
REM *** Make magenta transparent ***
SET IMAGE COLORKEY 255,0,255
REM *** Load texture images ***
No solution required.
Activity 32.25
Activity 32.21 No solution required.
REM *** Set display resolution ***
SET DISPLAY MODE 1280,1024,32 Activity 32.26
REM *** Seed random number generator ***
RANDOMIZE TIMER() REM *** Set display resolution ***
REM *** Load image *** SET DISPLAY MODE 1280, 1024,32
LOAD IMAGE "DoNot.bmp",1 REM *** Create cube ***
REM *** Make and position cube *** MAKE OBJECT CUBE 1,10
MAKE OBJECT CUBE 1, 40 REM *** Texture cube ***
REM *** Create detail mapping *** LOAD IMAGE "windmillshaped.tga",1
SET DETAIL MAPPING ON 1,1 TEXTURE OBJECT 1,1
POSITION OBJECT 1,25,0,100 REM *** Rotate cube ***
REM *** Colour cube red *** DO
COLOR OBJECT 1, RGB(255,0,0) REM *** IF key pressed, ghost ***
REM *** Rotate Cube *** IF INKEY$() <> ""
DO GHOST OBJECT ON 1,3
PITCH OBJECT DOWN 1,1.0 ENDIF
TURN OBJECT LEFT 1, 1.0 TURN OBJECT LEFT 1,1
REM *** 1 in 1000 of going green *** LOOP
IF RND(1000) = 500 REM *** End program ***
COLOR OBJECT 1, RGB(0,255,0) END
ENDIF
LOOP
REM *** End program *** Activity 32.27
END
No solution required.
Activity 32.22
Activity 32.28
No solution required.
No solution required.
Activity 32.23
Activity 32.29
No solution required.
REM *** Set display resolution ***
SET DISPLAY MODE 1280,1024,32
Activity 32.24 DrawGallows()
POINT CAMERA -150,10,0
REM *** Set display resolution *** WAIT KEY
SET DISPLAY MODE 1280,1024,32 END
REM *** Load texture images ***
LOAD IMAGE "textureWood.jpg",1
LOAD IMAGE "DoNot.bmp",2
REM *** Make and position cube *** FUNCTION DrawGallows()
MAKE OBJECT CUBE 1, 40 REM *** Set up names ***
POSITION OBJECT 1,25,0,100 REM *** Object names ***
REM *** Texture cube *** #CONSTANT GroundObj 901
TEXTURE OBJECT 1,1 #CONSTANT PlatformObj 902
REM *** Create background plain *** #CONSTANT VerticalPostObj 903
MAKE OBJECT PLAIN 2,100,100 #CONSTANT HorizontalPostObj 904
TEXTURE OBJECT 2, 2 #CONSTANT DiagonalPostObj 905
POSITION OBJECT 2,0,0,200 #CONSTANT TopStepObj 906
REM *** Start reflective value at 200 *** #CONSTANT MiddleStepObj 907
reflectivity = 200 #CONSTANT BottomStepObj 908
REM *** Make cube transparent *** #CONSTANT StepEdgeRightObj 909
GHOST OBJECT ON 1,0 #CONSTANT StepEdgeLeftObj 910
REM *** rotate cube *** REM *** Image names ***
DO #CONSTANT CobbleImg 901
PITCH OBJECT DOWN 1, 1.0 #CONSTANT PlanksImg 902
TURN OBJECT LEFT 1, 1.0 #CONSTANT WoodImg 903
FADE OBJECT 1,reflectivity REM *** Load texture images ***
REM *** Reduce reflectivity to zero *** LOAD IMAGE "TextureWood.jpg",PlanksImg
IF reflectivity > 0 LOAD IMAGE "CobbleStones.jpg",CobbleImg
DEC reflectivity LOAD IMAGE "Wood.jpg",WoodImg
ENDIF REM *** Create cobbled square ***
MAKE OBJECT PLAIN GroundObj,300,300
Activity 32.30
The changes to the cobbled square in the DrawGallows()
function are shown in bold below:
Activity 32.31
The sky should now be visible.
Activity 32.32
The sphere's texture is upside down and mirrored.
Activity 32.33
No solution required.
The camera in DarkBASIC Pro is a virtual one; totally invisible and with no chance
of accidentally appearing in the picture showing on the screen.
In this chapter we'll cover the DarkBASIC Pro statements that allow us to move the
main camera, create new cameras (so we can view the world from multiple
positions) and take full control of the view created on our screen.
We'll use the castle environment we created in the previous chapter to demonstrate
most of the camera statements.
Just as we might position, move, or rotate a 3D object, so we can perform the same
type of operation on the camera. By moving the camera and making it point in some
other direction, so we generate a new viewpoint on the screen.
In the diagram:
Only camera zero is created automatically. If you don't intend to create any others,
The position to which the camera is to be moved can be given using three separate
values or a single 3-element vector object containing the same three values. We'll
ignore the vector option until we cover 3D vectors in a later chapter. So, using the
POSITION CAMERA statement we could place the main camera at position
(10,20,30) using the line:
POSITION CAMERA 0,10,20,30
Activity 33.1
How does this affect the view when the program is executed?
For example, we could move the active camera forward 30 units using the line:
MOVE CAMERA 30
Activity 33.2
Replace the POSITION CAMERA statement in the last program with the line
MOVE CAMERA 50
Activity 33.3
FOR c = 1 TO 100
MOVE CAMERA 1
WAIT 50
NEXT c
Run the program and observe how the camera moves under the plane.
In the diagram:
For example, we could point the camera at the entrance to the castle using the line:
Activity 33.4
Add the above statement to your last program immediately before the FOR
loop used to move the camera.
Extend the FOR loop so that the camera moves right up to the castle's wall.
Camera z-axis +
x-axis -
+
In the diagram:
xangle is a real number giving the angle (in degrees) to which the
camera is to be rotated about its x-axis.
yangle is a real number giving the angle (in degrees) to which the
camera is to be rotated about its y-axis.
zangle is a real number giving the angle (in degrees) to which the
camera is to be rotated about its z-axis.
Rotation about the x-axis is the type of movement we would make filming a rocket
taking off or a diver jumping off a cliff. Rotation about the y-axis (also known as
panning) is the type of movement required to follow a racing car or a runner.
To make the camera point straight forward (that is, parallel to the main z-axis) we
would use the line:
ROTATE CAMERA 0,0,0
Activity 33.5
Remove the POINT CAMERA and FOR loop code from camera01.dbpro and
add the lines
Run the program and observe the effect produced. Change the code to also
create rotation about the other two axes.
FIG-33.6
The SET CAMERA
ROTATION Statement
SET CAMERA ROTATION
{ { XYZ
ZYX
camno
In the diagram:
Rather than perform three rotations in a single statement, we can rotate about a
single axis only using one of the following:
angle is a real number giving the angle (in degrees) to which the
object is to be rotated.
FIG-33.8 shows the result of rotating the active camera to 35.0o and -35.0o about
the x-axis.
FIG-33.8
The Effects of the
XROTATE Statement
In the diagram:
angle is a real number giving the angle (in degrees) to which the
object is to be rotated.
FIG-33.10 shows the result of rotating the active camera to 35.0o and -35.0o about
the y-axis.
FIG-33.10
The Effects of the
YROTATE
CAMERA Statement
In the diagram:
angle is a real number giving the angle (in degrees) to which the
object is to be rotated.
FIG-33.12 shows the result of rotating the active camera to 35.0o and -35.0o about
the z-axis.
We might require such a rotation if we wanted to show the view from the inside as
a car rolls over onto its roof.
The following statements allow relative rotation rather than absolute rotation.
FIG-33.13
The PITCH CAMERA
Statement
PITCH CAMERA
{ { DOWN
UP
camno , angle
In the diagram:
PITCH CAMERA DOWN moves the camera in the same direction as XROTATE
CAMERA using a negative angle.
FIG-33.14
The TURN CAMERA
Statement TURN CAMERA
{ { LEFT
RIGHT
camno , angle
LEFT, RIGHT Choose LEFT to make the camera rotate to the left;
choose RIGHT to make the camera rotate the right.
TURN CAMERA RIGHT moves the camera in the same direction as YROTATE
CAMERA using a positive angle. TURN CAMERA LEFT moves the camera in
the same direction as YROTATE CAMERA using a negative angle.
RIGHT
camno , angle
In the diagram:
LEFT, RIGHT Choose LEFT to make the camera roll to the left;
choose RIGHT to make the camera roll to the
right.
ROLL CAMERA RIGHT moves the camera in the same direction as ZROTATE
CAMERA using a positive angle. ROLL CAMERA LEFT moves the camera in the
same direction as ZROTATE CAMERA using a negative angle.
Activity 33.6
Create a new main section for camera01.dbpro, so that the camera can be
rotated using the keyboard. Each rotation should be by 1o in the required
direction. Start the camera with zero rotation. Control keys are as follows:
FIG-33.16
The CAMERA
POSITION Statement
In the diagram:
real
Activity 33.7
Modify your previous program so that the camera's angle of rotation about all
three local axes is always displayed on the screen.
In the diagram:
For example, we could change the output to occupy the area shown in FIG-33.19
using the statement:
Activity 33.8
Modify your last program to use the area specified in the line above for
camera output.
Move the camera angle displays into the black area of the screen. Does this
cause any problems?
The SET CAMERA ASPECT statement has the format shown in FIG-33.20.
FIG-33.20
The SET CAMERA SET CAMERA ASPECT camno , ratio
ASPECT Statement
In the diagram:
To correct the distortion caused by the SET CAMERA VIEW statement we need
the line
FIG-33.21
Correcting the Camera
Output Distortion
Activity 33.9
In your last program, correct the distortion shown in the camera display.
When the camera is set to a wide field of ... we see more of the surroundings but
view... things looks smaller than normal.
Zoom in to
reduce the
field of view
When the camera is set to a narrow field ... we see less of the surroundings but
of view... things look larger than normal.
Our own virtual camera can perform the same trick using the SET CAMERA FOV
statement which has the format shown in FIG-33.23.
FIG-33.23
SET CAMERA FOV camno , angle
The SET CAMERA
FOV Statement
In the diagram:
Activity 33.10
This effect is achieved using the SET CAMERA RANGE statement which has the
format shown in FIG-33.24.
In the diagram:
FIG-33.25
Restricting the Visible
Area to Between 50 and
600 Units from the Only objects in this area
Camera are visible to the camera
Camera
50
600
Activity 33.11
immediately before the first FOR loop and observe how this affects the view
displayed on the screen.
Summary
l Whenever 3D objects are used in a DarkBASIC Pro program, a default camera
is automatically created.
l The image shown on the screen is the picture transmitted by the default camera.
l Use ROTATE CAMERA to rotate the camera to absolute angles about all of its
three axes.
l Use XROTATE CAMERA to rotate the camera to an absolute angle about the
x-axis only.
l Use YROTATE CAMERA to rotate the camera to an absolute angle about the
y-axis only.
l Use ZROTATE CAMERA to rotate the camera to an absolute angle about the
z-axis only.
l Use PITCH CAMERA to rotate the camera by a number of degrees about its
x-axis.
l Use TURN CAMERA to rotate the camera by a number of degrees about its
y-axis.
l Use ROLL CAMERA to rotate the camera by a number of degrees about its
z-axis.
l Use CAMERA ANGLE to retrieve the camera's angle of rotation about a specific
axis.
l Use SET CAMERA VIEW to set how much of the screen the camera's picture
occupies.
l Use SET CAMERA ASPECT to set the aspect ratio of the camera's picture.
l Use SET CAMERA FOV to set the camera's field of view. This is equivalent to
using a zoom lens on a standard camera.
l Use SET CAMERA RANGE to set the nearest and furthest point visible to the
camera.
Activity 33.12
FUNCTION FallingObject()
REM *** Create textured transparent sphere ***
MAKE OBJECT SPHERE artifact,10
LOAD IMAGE "lattice.bmp",arttexture
TEXTURE OBJECT artifact,arttexture
SET OBJECT TRANSPARENCY artifact, 1
REM *** Position artifact out in sky ***
POSITION OBJECT artifact, -200,300,900
REM *** Point the object towards the ground ***
POINT OBJECT artifact,300,0,30
REM *** Move object, stopping when it hits the ground ***
DO
MOVE OBJECT artifact,8
IF OBJECT POSITION Y(artifact) <= 2
EXIT
ENDIF
LOOP
ENDFUNCTION
We can modify the program so that the camera is always pointing at the falling
object by adding the line
Modify your previous program as described above and observe how this
affects the behaviour of the camera.
In the diagram:
Collision boxes are collision is 0 or 1 and is used to specify how the camera
discussed in a later
chapter.
is to react to colliding with a static collision box.
This is one of the most complex statements in DarkBASIC Pro, so it's worth
spending a bit of time finding out just how each value in the statement affects the
results you achieve. To demonstrate how it works we'll track the falling object of
our previous program.
The x,y,z values give the position to be tracked, so in this case that means the
The distance and height values specify how far the camera is from the point being
tracked and the height above that point (see FIG-33.28).
FIG-33.28
The camera is not
rotated about the x-axis
The Camera Distance and by this statement...
Height ... which means it doesn’t
Camera point down at the object
distance automatically...
Object
(x,y,z)
This is not enough information to specify a single position for the camera, since any
point on a circle which is distance units from the object and height units above the
object will meet the criteria specified (see FIG-33.29).
distance distance
height height
y-axis
x-axis (x,y,z)
Object
z-axis
The final value required to create a unique position for the camera is the angle value
which specifies how far round the circle the camera is to be placed (see FIG-33.30).
distance
height
y-axis
x-axis (x,y,z)
Object
z-axis
FUNCTION FallingObject()
REM *** Create textured transparent sphere ***
MAKE OBJECT SPHERE artifact,10
LOAD IMAGE "lattice.bmp",arttexture
TEXTURE OBJECT artifact,arttexture
SET OBJECT TRANSPARENCY artifact, 1
REM *** Position artifact out in sky ***
POSITION OBJECT artifact, -200,300,900
REM *** Point the object towards the ground ***
POINT OBJECT artifact,300,0,30
REM *** Move object, stopping when it hits the ground ***
DO
MOVE OBJECT artifact, 8
SET CAMERA TO FOLLOW OBJECT POSITION X(artifact),
Ä
OBJECT POSITION Y(artifact),OBJECT POSITION Z(artifact),
Ä
0,20,0,1.0,1
IF OBJECT POSITION Y(artifact) <= 2
EXIT
ENDIF
LOOP
ENDFUNCTION
Notice that the angle and height values are set to zero, while the distance setting is
20.
Activity 33.15
Modify the program as follows, running the program after each change:
The smooth parameter determines how smoothly the camera moves from its current
position to the position required by the latest call to SET CAMERA TO FOLLOW
statement. Using a value of 1.0 the camera will jump almost instantly to the required
position, no matter what its current location. On the other hand, by using a value of
100.0, the camera may take hundreds of frames (that is, several seconds) to move
into position.
Activity 33.16
Set the smooth value in your last program's SET CAMERA TO FOLLOW
statement to 100.0 and re-run the program.
In our example, the smoothing factor affected the camera's movement from its
original position at (0,8,10) to its new position behind the sphere. The effect of
smoothing is also apparent if the object being tracked moves erratically in
We can even use this instruction with a high smoothing factor to make the camera
move slowly along a path to a given point. For example, the lines
will move the camera smoothly from the point (0,8,10) to within 10 units of the
point (-20,200,900).
Activity 33.17
#INCLUDE "Castle.dba"
#CONSTANT artifact 200
#CONSTANT arttexture 200
and check out how the camera moves and rotates between its starting and
finishing points.
You may have noticed that the camera moves straight through the first turret! Unless
it is playing the part of a ghostly apparition, we probably don't want this to happen.
To prevent this we use the final parameter of the SET CAMERA TO FOLLOW
statement which allows the camera to react to collisions. However, we need to know
how to handle collisions in general before examining this option, so we'll leave it
for a later chapter.
In the diagram:
For example, we could control the movement of camera 0, allowing a move of 0.5
units each time an up or down arrow key is pressed and with a rotation of 0.1o every
time a left or right arrow key is pressed using the line:
The statement must be placed in a loop structure (usually a DO..LOOP) so that the
statement is continually executed.
In LISTING-33.4 the user is allowed to move about the castle area by controlling
the camera.
REM *** Draw the castle and show falling object ***
DrawCastle()
FallingObject()
REM *** Move object, stopping when it hits the ground ***
DO
MOVE OBJECT artifact, 8
SET CAMERA TO FOLLOW OBJECT POSITION X(artifact),
Ä
OBJECT POSITION Y(artifact),OBJECT POSITION Z(artifact),
Ä
90,100,20,20,1
IF OBJECT POSITION Y(artifact) <= 2
EXIT
ENDIF
LOOP
ENDFUNCTION
Activity 33.18
As you can see from the results of the Activity, the player takes on ghost-like
qualities; being able to walk through solid objects! Luckily, there's an easy way to
stop this.
In the diagram:
For example, we could make camera zero halt when it comes within 2 units of an
object using the line:
Activity 33.19
before the DO loop in the main section of your last program, make the
user-control camera slide past any collisions.
We'll start by allowing the user to rotate the camera about both the x and y axes
using mouse movement. This is done by a function, PointCameraUsingMouse(),
which rotates a specified camera in response to mouse movement. The first version
of the routine is given below:
FUNCTION PointCameraUsingMouse(camno)
REM *** Get current camera angles ***
XcurrentAngle# = CAMERA ANGLE X(camno)
YcurrentAngle# = CAMERA ANGLE Y(camno)
REM *** Calculate the new angle based on mouse movement ***
XnewAngle# = XcurrentAngle#+ MOUSEMOVEY()
YnewAngle# = YcurrentAngle#+ MOUSEMOVEX()
REM *** Rotate camera ***
XROTATE CAMERA camno,XnewAngle#
YROTATE CAMERA camno,YnewAngle#
ENDFUNCTION
The routine stores the current angles of rotation and then calculates new angles
based on the movement of the mouse. Notice that mouse movement in the y
direction changes the angle of rotation about the x-axis, while mouse movement in
the x direction modifies the angle about the y-axis!
The routine is put to use in LISTING-33.5 which allows the user total freedom of
rotation in both the x and y axes.
Activity 33.20
Rotation is probably a bit too sensitive to mouse movement but we can overcome
this by reducing the effect of the mouse move values by changing the new angle
calculation statements to:
Rotation about the y-axis should be kept in the range 0 to 359.9 and we can do this
by applying the WRAPVALUE statement before assigning the value of the new
angle:
Given the restriction of a human's head movements, rotation about the x-axis should
probably remain within the range -90 to +90 so this angle should be calculated using
the lines:
Activity 33.21
Make the changes described above to your previous program and check out
how this affects the performance of the camera.
Now that we can point the camera in any direction, the next thing we need to do is
Activity 33.22
As we can see from the last Activity, the CONTROL CAMERA USING
ARROWKEYS statement always returns the camera to zero degrees rotation about
the x-axis, effectively stopping us from pointing the camera up or down.
One way to deal with this is to write our own movement routine. The routine given
below takes the camera involved and the distance moved each time as parameters.
The camera moves in the direction it is pointing by pressing the left mouse button;
if the right mouse button is pressed, the camera moves backwards.
Activity 33.23
Add the above function to your last program and replace the CONTROL
CAMERA USING ARROWKEYS line with the code
MoveCameraUsingMouse(0,1)
This gives us more freedom of movement than we might want for a player, but it is
useful when checking the layout of our virtual world.
Activity 33.24
By temporarily rotating the camera to zero degrees about the x-axis during
movement, modify the MoveCameraUsingMouse() function so that movement
is always parallel to the x-z plane irrespective of camera elevation.
To stop the camera moving outside the modelled area, modify the
MoveCameraUsingMouse() function to limit the position of the camera to be
within the area bounded by the x-z points (-50,0) (500,700).
Summary
l Use AUTOCAM OFF to stop the camera being positioned automatically by the
program.
l Use SET CAMERA TO FOLLOW to make the camera move smoothly so that
it is positioned to focus on a specific spot.
MAKE CAMERA 1
DrawCastle()
Why does the grass and path not show in the image from the second camera?
Notice that the background has turned green! Camera zero defaults to a blue
background, while camera 1's default background is always green.
Why did the background change colour when we saw the output from the second
camera? It turns out that the COLOR BACKDROP statement specifies the
background colour for the current camera only. In fact, the BACKDROP and
COLOR BACKDROP statements have a syntax slightly different from that given
back in volume 1, because both allow a camera to be specified. The updated version
of each statement is given below.
In the diagram:
To set the background colour for camera 1 to black we would use the statement
but for other colours we would probably use an RGB expression, as in the line:
FIG-33.35
The BACKDROP
Statement BACKDROP [ camno ,
{ ON
OFF
Activity 33.27
Modify your last program so that the background colour for camera 1 matches
that of camera zero.
In the diagram:
When a program begins, camera zero is the active camera. Camera zero remains the
active camera even after other cameras have been created. Once a new active camera
has been specified using SET CURRENT CAMERA, any subsequent
camera-related statement which does not explicitly specify a camera number will
be assumed to be acting on this camera.
DrawCastle()
Activity 33.28
In the diagram:
DELETE CAMERA 1
Since the output shown does change when a new camera is created, the only way
to switch between cameras is to delete and then recreate a camera. We also need to
save the position of the camera and the direction in which it is pointing so that the
recreated camera is correctly placed.
FUNCTION SwitchCamera(camno)
REM *** Save camera position ***
x# = CAMERA POSITION X(camno)
y# = CAMERA POSITION Y(camno)
z# = CAMERA POSITION Z(camno)
REM *** Save camera direction ***
angx# = CAMERA ANGLE X(camno)
angy# = CAMERA ANGLE Y(camno)
angz# = CAMERA ANGLE Z(camno)
REM *** Delete and recreate camera ***
DELETE CAMERA camno
MAKE CAMERA camno
REM *** Set camera as active camera ***
SET CURRENT CAMERA camno
REM *** Restore original position and direction ***
Obviously, the camera in question must already exist before the function is called.
Ideally, the function should start with the lines
After the SwitchCamera() function has been called, it will be necessary to reset the
background colour. For example, to switch to camera 2 with a red background we
would use the lines:
SwitchCamera(2)
COLOR BACKDROP RGB(64,0,128)
BACKDROP ON
DrawCastle()
Activity 33.29
FIG-33.38
Simultaneous Output
from Multiple Cameras
DrawCastle()
Activity 33.30
Modify the program so that the user can control movement of camera 0 using
the mouse.
Modify the program again so that camera 1 slowly rotates to the left.
In the diagram:
We could clear the view area of camera 1 to red using the line:
CLEAR CAMERA VIEW 1, RGB(255,0,0)
Since the camera's output is recalculated for every frame, and the effect of the
CLEAR CAMERA VIEW statement only lasts for a single frame, there doesn't
seem to be much use for this statement.
l When a new camera is created, the output of that camera is displayed on the
screen.
l Any statement in which the camera number is omitted is assumed to relate to the
active camera.
l The output of multiple cameras can appear on the screen at the same time by
allocating different areas of the screen to each camera.
l Use CLEAR CAMERA VIEW to clear the camera's output area using a specified
colour.
The Statements
The SET CAMERA TO IMAGE Statement
A rather neat trick is to display the output from a camera on the surface of an object.
This allows us to create things like mirrors or TV monitors. To achieve this affect
we need to first transfer the camera's output to an image object and then texture the
3D object with that image. The first stage in this process is achieved using the SET
CAMERA TO IMAGE statement whose format is given in FIG-33.40.
In the diagram:
For example, we could output camera 1's display to image 10 which is set to a width
of 128 by 128 using the line:
SET CAMERA TO IMAGE 1,10,128,128
If image 10 does not exist when the statement is executed, it will be created.
However, if it does exist, its contents will be modified by this statement.
Next we need to use the image to texture a 3D object. For example, we could texture
object 5 with our new image using the line:
FUNCTION CameraOutputToObject(camno,objno,imgno)
SET CAMERA TO IMAGE camno,imgno,512,512
TEXTURE OBJECT objno,imgno
ENDFUNCTION
If you wanted to allow the dimensions of the image to be set, these could be added
to the parameter list for the function.
The program in LISTING-33.10 makes use of this routine to display the output of
camera 1 onto a cube placed in the grassy area in front of the castle.
LISTING-33.10 #INCLUDE "Castle.dba"
#INCLUDE "Camera.dba"
Showing Camera Output
on an Object's Surface REM *** set up screen ***
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
DrawCastle()
CreateReflectiveCube()
FUNCTION CreateReflectiveCube()
REM *** Make cube ***
MAKE OBJECT CUBE 800,10
POSITION OBJECT 800,200,5.0001,50
REM *** Create camera inside cube ***
MAKE CAMERA 1
POSITION CAMERA 1,200,5.001,50
POINT CAMERA 1, -200,5.001,50
SET CAMERA VIEW 1, 0,0,10,10
COLOR BACKDROP 1, RGB(255,0,0)
BACKDROP ON 1
REM *** Texture cube with camera's output ***
CameraOutputToObject(1,800,222)
ENDFUNCTION
Activity 33.31
Activity 33.32
In your previous program, replace the cube with a plane. The plane should be
perpendicular to the grass and facing in the same direction as camera 1.
Modify the main section so that the plane rotates slowly about its y-axis.
In the diagram:
We can make use of this statement in the last program, making camera 1 always
point in the same direction as the plane. That way the illusion of a mirror is
maintained. We can achieve the effect using the statement:
The statement needs to be executed each time the object specified in the statement
moves.
Activity 33.33
In your last program, add the line given above as the last statement within the
DO..LOOP structure and observe how the image reflected by the plane
changes as the plane rotates.
The following function turns all the trees in the castle scene to face away from the
player (hence they always have their back to the player).
FUNCTION OrientTrees()
FOR c = tree1 TO tree1 + 30
SET OBJECT TO CAMERA ORIENTATION c
NEXT c
ENDFUNCTION
Activity 33. 34
Add a call to the function as your last statement in the DO..LOOP structure in
the main section of the program.
This effect can be toggled on or off in DarkBASIC Pro using the LOCK OBJECT
statement. LOCK OBJECT ON locks a specified object to a fixed position on the
screen no matter how the camera moves. To return an object to normal, use the
LOCK OBJECT OFF option. The statement has the format shown in FIG-33.43.
FIG-33.43
The LOCK OBJECT
Statement LOCK OBJECT
{ ON
OFF
objno
In the diagram:
Once this command has been executed, the objected specified will stay at exactly
the same spot on the screen irrespective of camera movement, but its position can
still be modified using statements such as POSITION OBJECT and ROTATE
OBJECT.
A background cube gives some depth to the program and the user can move the
camera using the arrow keys.
FUNCTION CreateWand()
REM *** Assign ID names ***
#CONSTANT wandobj 777
#CONSTANT wandimg 777
REM *** Make wand ***
MAKE OBJECT CYLINDER wandobj,1
LOAD IMAGE "wand.jpg",wandimg
TEXTURE OBJECT wandobj,wandimg
REM *** Fix wand to screen ***
LOCK OBJECT ON wandobj
REM *** Scale, rotate and position the wand ***
SCALE OBJECT wandobj, 10,100,10
XROTATE OBJECT wandobj,45
POSITION OBJECT wandobj,0.5,-1,2
ENDFUNCTION
Activity 33.35
Test the result. Are there any problems with the mirrored plane?
There are a couple of camera statements that make use of 3D vectors. These
statements are just alternatives to other camera statements we've already covered,
but are included here for completeness. 3D vectors themselves are covered in a later
chapter.
In the diagram:
For example, we could store the current position of camera zero in a 3D vector using
the lines:
In the diagram:
For example, we could store the current rotation details of camera zero in a 3D
vector using the lines:
Summary
l Use SET CAMERA TO IMAGE to send a camera's output to an image object.
REM *** Include DrawCastle() function *** To make the camera move right up to the castle wall,
#INCLUDE "castle.dba" change the FOR loop to read
REM *** Set screen resolution ***
SET DISPLAY MODE 1280,1024,32 FOR c = 1 TO 750
DrawCastle()
WAIT KEY
POSITION CAMERA 0,0,5,50 Activity 33.5
WAIT KEY
END REM *** Include DrawCastle() function ***
#INCLUDE "castle.dba"
Make sure you have copied Castle.dba into the same folder REM *** Set screen resolution ***
as this program. SET DISPLAY MODE 1280,1024,32
DrawCastle()
WAIT KEY
The camera has moved closer to the castle so that the 3D REM *** Turn camera by increments ***
objects now occupy the whole screen. FOR angle = -45 TO 45
ROTATE CAMERA angle,0,0
WAIT 10
Activity 33.2 NEXT angle
REM *** End program ***
The program should now read: WAIT KEY
END
REM *** Include DrawCastle() function ***
#INCLUDE "castle.dba" To rotate the camera about the y-axis change the
REM *** Set screen resolution ***
ROTATE CAMERA line to
SET DISPLAY MODE 1280,1024,32
DrawCastle()
WAIT KEY ROTATE CAMERA 0,angle,0
MOVE CAMERA 50
WAIT KEY Rotation about the z-axis requires the line
END
ROTATE CAMERA 0,0,angle
The camera moves 50 units from its starting position in the
direction it is facing when a key is pressed. The jump from
the starting position to the final position is instant. Activity 33.6
REM *** Include DrawCastle() function ***
#INCLUDE "castle.dba"
Activity 33.3 REM *** Set screen resolution ***
SET DISPLAY MODE 1280,1024,32
The latest version of the program reads: DrawCastle()
REM *** Start with zero rotation ***
REM *** Include DrawCastle() function *** ROTATE CAMERA 0,0,0
#INCLUDE "castle.dba" DO
REM *** Set screen resolution *** REM *** Rotate camera using keys ***
SET DISPLAY MODE 1280,1024,32 SELECT INKEY$()
DrawCastle() CASE "z"
WAIT KEY TURN CAMERA LEFT 1
REM *** Move camera by increments *** ENDCASE
FOR c = 1 TO 50 CASE "x"
MOVE CAMERA 1 TURN CAMERA RIGHT 1
WAIT 50 ENDCASE
NEXT c CASE "'"
WAIT KEY PITCH CAMERA UP 1
END ENDCASE
CASE "/"
PITCH CAMERA DOWN 1
Activity 33.4 ENDCASE
CASE "n"
REM *** Include DrawCastle() function *** ROLL CAMERA LEFT 1
#INCLUDE "castle.dba" ENDCASE
REM *** Set screen resolution *** CASE "m"
SET DISPLAY MODE 1280,1024,32 ROLL CAMERA RIGHT 1
DrawCastle() ENDCASE
WAIT KEY ENDSELECT
REM *** Point camera in direction of move ** LOOP
POINT CAMERA -25,5,700 REM *** End program ***
REM *** Move camera by increments *** END
FOR c = 1 TO 100
MOVE CAMERA 1
WAIT 50
Activity 33.16 The camera now moves more slowly in reaction to mouse
movements.
The SET CAMERA TO FOLLOW line in your program
should read
Activity 33.22
SET CAMERA TO FOLLOW
ÄOBJECT POSITION X(artifact), The updated program is now:
ÄOBJECT POSITION Y(artifact),
ÄOBJECT POSITION Z(artifact), #INCLUDE "Castle.dba"
Ä90,100,20,100.0,1 REM *** set up screen ***
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
The camera now swings smoothly into place from its
DrawCastle()
original position, rather than jumping to the new position REM *** Re-position camera ***
abruptly. POSITION CAMERA 0,8,10
POINT CAMERA 0,8,200
With a smooth value of 20 the jump between the original DO
position and the new position is faster than with a setting PointCameraUsingMouse(0)
of 100. CONTROL CAMERA USING ARROWKEYS 0,0.5,0
LOOP
REM *** End program ***
END
Activity 33.17
No solution required. REM *** Rotates the active camera ***
FUNCTION PointCameraUsingMouse(camno)
REM *** Get current camera angles ***
XcurrentAngle# = CAMERA ANGLE X(camno)
Activity 33.18 YcurrentAngle# = CAMERA ANGLE Y(camno)
XnewAngle# = XcurrentAngle#
Yes. The camera can move straight through solid objects.
Ä+ MOUSEMOVEY()*0.1
IF XnewAngle# < -90
XnewAngle# = -90
Activity 33.19 ELSE
IF XnewAngle# > 90
No solution required. XnewAngle# = 90
ENDIF
ENDIF
Activity 33.20 YnewAngle# = WRAPVALUE(YcurrentAngle#
The camera turns too quickly to gain accurate control.
Ä+ MOUSEMOVEX()*0.1)
REM *** Rotate camera ***
XROTATE CAMERA camno,XnewAngle#
YROTATE CAMERA camno,YnewAngle#
Activity 33.21 ENDFUNCTION
#INCLUDE "Castle.dba"
REM *** set up screen *** The camera can no longer point up or down.
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
DrawCastle() Activity 33.23
REM *** Re-position camera ***
POSITION CAMERA 0,8,10 #INCLUDE "Castle.dba"
POINT CAMERA 0,8,200 REM *** set up screen ***
DO SET DISPLAY MODE 1280,1024,32
PointCameraUsingMouse(0) AUTOCAM OFF
LOOP DrawCastle()
REM *** End program *** REM *** Re-position camera ***
END POSITION CAMERA 0,8,10
REM *** Rotates the active camera *** POINT CAMERA 0,8,200
FUNCTION PointCameraUsingMouse(camno) DO
REM *** Get current camera angles *** PointCameraUsingMouse(0)
XcurrentAngle# = CAMERA ANGLE X(camno) MoveCameraUsingMouse(0,1)
YcurrentAngle# = CAMERA ANGLE Y(camno) LOOP
#INCLUDE "Castle.dba"
#INCLUDE "Camera.dba" Activity 33.33
REM *** set up screen ***
SET DISPLAY MODE 1280,1024,32 The DO..LOOP in the main section should now read:
AUTOCAM OFF
DrawCastle() DO
REM *** Re-position camera *** PointCameraUsingMouse(2)
POSITION CAMERA 0,8,10 MoveCameraUsingMouse(2,1)
POINT CAMERA 0,8,200 TURN OBJECT LEFT 800,0.1
REM *** Create a second camera ...*** SET CAMERA TO OBJECT ORIENTATION 1,800
MAKE CAMERA 1 LOOP
POSITION CAMERA 1,50,250,900
POINT CAMERA 1,200,0,0
REM *** ... with its own output area ***
SET CAMERA VIEW 1, 800,0,1279,300
Activity 33.34
REM *** Control camera movement *** #INCLUDE "Castle.dba"
DO #INCLUDE "Camera.dba"
PointCameraUsingMouse(0) REM *** set up screen ***
MoveCameraUsingMouse(0,0.5) SET DISPLAY MODE 1280,1024,32
TURN CAMERA LEFT 1,0.1 AUTOCAM OFF
LOOP DrawCastle()
REM *** End program *** CreateReflectivePlane()
WAIT KEY REM *** Create and use camera 2 ***
END MAKE CAMERA 2
POSITION CAMERA 2,0,8,10
Remember to copy Camera.dba to the current folder. POINT CAMERA 2,0,8,200
SwitchCamera(2)
REM *** Let user control camera ***
Activity 33.31 DO
PointCameraUsingMouse(2)
No solution required. MoveCameraUsingMouse(2,1)
TURN OBJECT LEFT 800,0.1
SET CAMERA TO OBJECT ORIENTATION 1,800
Activity 33.32 OrientTrees()
LOOP
#INCLUDE "Castle.dba" REM *** End program ***
#INCLUDE "Camera.dba" END
REM *** set up screen ***
SET DISPLAY MODE 1280,1024,32 FUNCTION CreateReflectivePlane()
AUTOCAM OFF REM *** Make cube ***
DrawCastle() MAKE OBJECT PLAIN 800,10,10
CreateReflectivePlane() POSITION OBJECT 800,200,5.001,50
REM *** Create and use camera 2 *** POINT OBJECT 800,-200,5,50
MAKE CAMERA 2 REM *** Camera in middle of plane***
POSITION CAMERA 2,0,8,10 MAKE CAMERA 1
POINT CAMERA 2,0,8,200 POSITION CAMERA 1,200,5,50
SwitchCamera(2) POINT CAMERA 1, -200,5,50
REM *** Let user control camera *** SET CAMERA VIEW 1, 0,0,10,10
DO COLOR BACKDROP 1, RGB(255,0,0)
PointCameraUsingMouse(2) BACKDROP ON 1
MoveCameraUsingMouse(2,1) REM *** Texture with camera's output ***
TURN OBJECT LEFT 800,0.1 CameraOutputToObject(1,800,222)
LOOP ENDFUNCTION
REM *** End program ***
END FUNCTION CameraOutputToObject
Ä(camno,objno, imgno)
FUNCTION CreateReflectivePlane() SET CAMERA TO IMAGE camno,imgno,512,1024
REM *** Make plane *** TEXTURE OBJECT objno,imgno
MAKE OBJECT PLAIN 800,10,10 ENDFUNCTION
POSITION OBJECT 800,200,5.001,50
POINT OBJECT 800,-200,5,50 FUNCTION OrientTrees()
REM *** Create camera in mid-plane*** FOR c = tree1 TO tree1 + 30
MAKE CAMERA 1 SET OBJECT TO CAMERA ORIENTATION c
POSITION CAMERA 1,200,5,50 NEXT c
POINT CAMERA 1, -200,5,50 ENDFUNCTION
SET CAMERA VIEW 1, 0,0,10,10
COLOR BACKDROP 1, RGB(255,0,0)
BACKDROP ON 1 Activity 33.35
REM *** Camera's output as texture ***
CameraOutputToObject(1,800,222) #INCLUDE "Castle.dba"
ENDFUNCTION #INCLUDE "Camera.dba"
REM *** set up screen ***
FUNCTION CameraOutputToObject SET DISPLAY MODE 1280,1024,32
(camno,objno, imgno) AUTOCAM OFF
SET CAMERA TO IMAGE camno,imgno,512,1024 DrawCastle()
TEXTURE OBJECT objno,imgno CreateReflectivePlane()
FUNCTION CreateReflectivePlane()
REM *** Make cube ***
MAKE OBJECT PLAIN 800,10,10
POSITION OBJECT 800,200,5.001,50
POINT OBJECT 800,-200,5,50
REM *** Camera in middle of plane***
MAKE CAMERA
POSITION CAMERA 1,200,5,50
POINT CAMERA 1, -200,5,50
SET CAMERA VIEW 1, 0,0,10,10
COLOR BACKDROP 1, RGB(255,0,0)
BACKDROP ON 1
REM *** Texture with camera's output ***
CameraOutputToObject(1,800,222)
ENDFUNCTION
FUNCTION CameraOutputToObject
Ä(camno,objno, imgno)
SET CAMERA TO IMAGE camno,imgno,512,1024
TEXTURE OBJECT objno,imgno
ENDFUNCTION
FUNCTION OrientTrees()
FOR c = tree1 TO tree1 + 30
SET OBJECT TO CAMERA ORIENTATION c
NEXT c
ENDFUNCTION
FUNCTION CreateWand()
REM *** Assign ID names ***
#CONSTANT wandobj 777
#CONSTANT wandimg 777
REM *** Make wand ***
MAKE OBJECT CYLINDER wandobj,1
LOAD IMAGE "wand.jpg",wandimg
TEXTURE OBJECT wandobj,wandimg
REM *** Fix wand to screen ***
LOCK OBJECT ON wandobj
REM *** Scale, rotate & position wand ***
SCALE OBJECT wandobj, 10,100,10
XROTATE OBJECT wandobj,45
POSITION OBJECT wandobj,0.5,-1,2
ENDFUNCTION
Fog
Positioning Lights
Types of Lighting
Types of Lighting
There are several types of lighting, each of which are described below.
Ambient Lighting
An ambient light is one with no obvious source and no obvious direction. Ambient
light is everywhere and appears to come from every direction. It's the sort of light
you might get on a heavily overcast day in the middle of winter.
Point Lighting
Point lighting is light that originates from a specific point in space and casts light
equally in all directions. The further away we move from the source, the duller the
light becomes. This is similar to a naked light bulb.
Spot Lighting
A spot light originates from a point in
FIG-34.1
space and the light itself shines in a
Lighting specific direction creating a cone-shaped
light. The angle over which the light
shines can be set and the light consists of
a primary and secondary area, with the
Lighting secondary area containing a duller light
than the primary area. Again the light
diminishes the further we travel from the
source. We can see examples of
spotlights in old war movies, the
spotlights shining into the skies
searching for enemy bombers.
Ambient: Light comes from every direction; Point: light comes from a single point;
brightness constant over distance. brightness reduces over distance. Directional Lighting
A directional light originates from an
infinitely far point, but its brightness
does not diminish over distance.
Sunshine on a cloudless day is the
obvious example for this type of lighting.
Spot: light comes from a single point; Directional: light comes from a distant point;
Fig-34.1 shows the basic characteristics
focuses on a specific area;
light reduces over distance.
rays are parallel; of each type of lighting.
brightness constant
FIG-34.2
Shading Caused by the
Default Directional Light
Every light source in DarkBASIC Pro must be allocated an ID number (only the
ambient light is exempt - it has no ID value). The default directional light, created
automatically, is assigned the ID number zero.
In the diagram:
HIDE LIGHT 0
would switch off the default directional light, leaving only the weak ambient light.
The program in LISTING-34.1 shows a cube with default lighting. By pressing any
key, the default directional light is switched off, leaving only the ambient light.
Activity 34.1
In the diagram:
In the diagram:
For example, we could switch off the ambient light using the statement:
Activity 34.2
Modify your previous program so that the ambient light level gradually
changes from an initial setting of 100 down to 0.
In the diagram:
For example, we could change the ambient light to red using the line:
Activity 34.3
In the diagram:
Like any other 3D object, lights are initially positioned at (0,0,0). When first
created, every light is a white, point light.
In LISTING-34.2 the default directional light is switched off and a new light
created. The effect of the new light can be seen on the side of the cube displayed
by the program.
Activity 34.4
In the diagram:
red
y ,z green , blue
In the diagram:
Activity 34.5
In the diagram:
For example, we could move light 1 to position (2,3,4) using the line:
Activity 34.6
In your previous program, make light 1 travel slowly away from position
(0,0,0) by decrementing its y-ordinate value (use a DO..LOOP structure so the
light continues to move indefinitely). Remember to use SYNC to update the
screen.
For example, we could set the light from light 1 to fade to complete darkness at a
Activity 34.7
In your previous program, reduce the range of light 1 to 50 and observe how
this affects the time it takes for the light on the cubes to fade.
In the diagram:
inner outer
angle angle
light source
Unlike point and spot lights, a directional light has no specific location point. Light
comes from an undefined point with all rays parallel. However, in order to easily
specify a direction for these rays, when a directional light is created it is assumed
to be at position (0,0,0). The direction of the light emitted is then parallel to the line
drawn between (0,0,0) and (x,y,z) - this last point being given in the SET
DIRECTIONAL LIGHT statement. For example, we could transform an existing
light 1 to a directional light and have its rays shining from directly above using the
line:
Note that the length of the line between (0,0,0) and the point given when creating
the light has no effect on the brightness of the light. Hence, the line
creates light coming from exactly the same direction and brightness as the previous
statement.
In the diagram:
The program in LISTING-34.3 shines a spot light onto a sphere and then moves the
light gradually away from the sphere.
Activity 34.8
Replace the sphere with a 20 by 20 plane. How does this affect the results?
Why has the spot light stopped working when we changed from a sphere to a plane?
Activity 34.9
We could achieve the same effect using the rather more complex line:
DO
POSITION LIGHT 1,OBJECT POSITION X(10),OBJECT POSITION Y(10),
Ä
OBJECT POSITION Z(10)
MOVE OBJECT RIGHT 10,1
SYNC
WAIT 1
LOOP
Activity 34.10
To make spot light 1 point in the same direction as object 10 we would use the
statement:
Pointing a Light in the REM *** Create spot light at cube's position ***
Same Direction as an MAKE LIGHT 1
Object SET SPOT LIGHT 1,10,30
POSITION LIGHT 1, 0,0,-10
POINT LIGHT 1,0,0,0
SET LIGHT RANGE 1,100
Activity 34.11
integer
In the diagram:
integer
In the diagram:
real
In the diagram:
integer
In the diagram:
1 - directional light
2 - point light
3 - spot light
real
In the diagram:
A real value is returned by this statement. We could record the exact position of
light 1 using the following statements:
real
A real value is returned by this statement. The value returned always lies between
-1 and 1. FIG-34.26 shows how that value is calculated.
FIG-34.26
Light Direction
How the Value Returned
by LIGHT DIRECTION
is Calculated
A light (spot or directional) is created at a point Imagine a beam, exactly 1 unit in length, pointing
in space. in the direction in which the light is facing.
End point
The end point of this line matches the values As the beam changes direction, so the values
returned by appropriate calls to the LIGHT returned by LIGHT DIRECTION also change to
DIRECTION statement. match the new position of the end point.
Activity 34.12
Fog
The FOG Statement
We can create a fog-like effect using FOG ON and disable the effect using FOG
OFF. The FOG statement has the format shown in FIG-34.27.
FIG-34.27
The FOG Statement
FOG
{ ON
OFF
red
y ,z green , blue
In the diagram:
In the diagram:
The program in LISTING-34.6 demonstrates the use of the fog statements. The
program creates an enclosing cube, inside which a line of spheres recede into the
distance. The fog is switched on and the fog density varied.
LISTING-34.6 REM *** Set up screen ***
SET DISPLAY MODE 1280,1024,32
Creating Fog COLOR BACKDROP RGB(20,20,20)
BACKDROP ON
Activity 34.13
Activity 34.14
In your previous program, create a moving cube using the following code:
WAIT 2000
MAKE OBJECT CUBE 11,1
POSITION OBJECT 11,5,0,0
POINT OBJECT 11,5,0,60
FOR move = 1 TO 60
MOVE OBJECT 11,1
WAIT 10
NEXT move continued on next page
The code should be inserted immediately before the final WAIT KEY
statement.
Remove the FOR loop which varies the fog distance and replace it with a
fixed fog distance of 50.
before the cube begins moving and check what difference this makes to the
cube's appearance.
TABLE-34.1
Statement Parameters Returned Description
Lights and 3D Vectors
Statements SET VECTOR3 TO LIGHT POSITION vno, lightno vno The3D vector with ID vno is set to
hold the coordinates of lightno.
SET VECTOR3 TO LIGHT ROTATION vno, lightno vno The 3D vector with ID vno is set to
hold lightno's rotation about each axis.
Summary
l There are four types of lighting in DarkBASIC Pro:
l Use SET AMBIENT LIGHT to set the brightness of the default ambient light.
l Use MAKE LIGHT to create a new light. The light created will be a point light.
l Use POSITION LIGHT to place a light at a specific point in space (point and
spot only).
l Use SET LIGHT RANGE to specifiy how far a light's brightness carries (point
and spot only).
l Use POINT LIGHT to specify the direction of a light (spot and directional only).
l Use ROTATE LIGHT to rotate the direction of a light (spot and directional only).
l Use SET LIGHT TO OBJECT ORIENTATION to make a light point in the same
direction as a specified object.
l Use LIGHT RANGE to check the range of a specified light (spot and point only).
l Use LIGHT POSITION to determine the position of a specified light (spot and
point only).
l Use LIGHT DIRECTION to determine the end point of a 1 unit beam emitted
from a specified light assumed to be at position (0,0,0) (spot and directional
only).
l Spot and point lights only work effectively on surfaces constructed from many
polygons.
l Use FOG DISTANCE to set the distance at which fog becomes opaque.
Statement Light
A D P S
COLOR AMBIENT LIGHT
COLOR LIGHT
DELETE LIGHT
HIDE LIGHT
LIGHT DIRECTION
LIGHT EXIST
LIGHT POSITION
LIGHT RANGE
LIGHT TYPE
LIGHT VISIBLE
MAKE LIGHT
POINT LIGHT
POSITION LIGHT
ROTATE LIGHT
SET AMBIENT LIGHT
Activity 34.14
REM *** Set up screen ***
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(20,20,20)
BACKDROP ON
REM *** Position camera ***
AUTOCAM OFF
POSITION CAMERA 20,0,60
POINT CAMERA 0,0,0
REM *** Switch off directional light ***
HIDE LIGHT 0
REM *** Make and position spheres ***
MAKE OBJECT SPHERE 1,4,60,60
FOR objno = 2 to 8
CLONE OBJECT objno,1
POSITION OBJECT objno,0,0,objno*5
NEXT objno
REM *** Create an enclosing cube ***
MAKE OBJECT CUBE 10,-200
POSITION OBJECT 10,0,-3,0
REM *** Create a light ***
MAKE LIGHT 1
POSITION LIGHT 1,10,1,40
SET LIGHT RANGE 1,50
REM *** Set up Fog ***
WAIT 1000
FOG ON
REM *** Colour fog ***
FOG COLOR RGB(60,10,90)
REM *** Set fog distance to 50 ***
FOG DISTANCE 50
REM *** fly in cube ***
WAIT 2000
MAKE OBJECT CUBE 11,1
POSITION OBJECT 11,5,0,0
POINT OBJECT 11,5,0,60
FOR move = 1 TO 60
MOVE OBJECT 11,1
WAIT 50
NEXT move
REM *** End program ***
WAIT KEY
END
Manipulating Limbs
To differentiate between the two formats we will refer to them as objects and
meshes. The most obvious difference between a mesh and an object is that meshes
are not visible on screen and do not retain texture details!
Meshes in themselves are of limited use. Their main purpose is as building blocks
for more complex shapes, as we'll see later in this chapter.
Handling Meshes
The MAKE MESH FROM OBJECT Statement
To create a mesh we need to convert an existing object to mesh format. This is done
using the MAKE MESH FROM OBJECT statement which has the format shown
in FIG-35.1.
FIG-35.1
The MAKE MESH
MAKE MESH FROM OBJECT meshno , objno
FROM OBJECT
Statement
In the diagram:
The program in LISTING-35.1 creates a sphere and uses this to create a mesh; the
sphere is then deleted, leaving us with a mesh, but nothing visible on the screen.
Activity 35.1
In the diagram:
Files are saved in DirectX format and, therefore, should be given a .x extension
when being named.
For example, to save mesh 1 to a file named sphere01.x in a folder named meshdata
on the C: drive, we would use the line:
SAVE MESH "C:\meshdata\sphere01.x",1
If the file already exists, its existing data will be overwritten; if the file does not
exist, it will be created. Any folders named in the path details must already exist or
the save operation will fail.
If path details are omitted, then the file will be saved in the current folder - normally,
this will be the folder containing the program code.
Activity 35.2
Modify your last program so that the sphere created is saved in a file called
sphere01.x in the current project's folder.
Modify the program to texture the sphere using marble.bmp before converting
it to a mesh. Save the resulting mesh in file sphere02.x.
In the diagram:
For example, we could load the file sphere01.x (as created by the last program) into
a mesh with ID 1 using the line:
LOAD MESH "C:\limbs01\sphere01.x",1
In the diagram:
Activity 35.3
Make sure that the files sphere01.x, sphere02.x and marble.bmp have been
copied into the current project's folder.
After a mesh has been used to create an object, we could use this command to delete
the original mesh.
integer
In the diagram:
Summary
l A mesh is a 3D shape held in a different format from the more usual 3D object.
l When creating an object from a mesh, a texture can be applied to the object once
it is created.
Getting Started
At the core of any model is the basic root object. This can be created in the normal
way. We'll start by creating a sphere object:
MAKE OBJECT SPHERE 1,10
Now we can create a second object and convert it to a mesh, deleting the object
when we're done:
In the diagram:
For example, we could join our box mesh to the sphere object using the line
The program in LISTING-35.3 joins a box mesh to a sphere object and then allows
the user to move the camera to view the resulting model.
Activity 35.4
Notice that the limb's centre is positioned at the centre of the object to which it is
joined.
Once we have created a group object by adding a limb to a standard object, all the
items within that group become linked. As a result, if the root object is moved, its
limbs will also move.
Run the program and check that the sphere's limb moves along with the sphere.
In fact, any other operation which is applied to the root object, affects all the items
in the group object. For example, using the line HIDE OBJECT 1 in the last
program, would hide not only the sphere, but also the limb attached to it. If we
colour or texture the object, its limbs will also take on the same colour or texture.
Activity 35.6
Using the WAIT KEY statement to initiate each action, modify your last
program so that the root object changes colour to red and then is textured
using the image marble.bmp.
In the diagram:
For example, in the last program we could create a new object, 5, from limb number
1 of group object 1 using the line:
The new object will not retain any colour or texture details from the root object.
In your last program, create object 2 from limb 1 of the group object. Move
the newly created object to position (20,0,0).
Once you've tested the program, return the program to its original coding.
For example, we could move the box attached to the sphere to the back of that sphere
(a distance of 5 units along the z-axis) using the line:
Activity 35.8
Add the above line to your last program immediately after the limb is attached
to the sphere. How does this change the position of the limb?
Now we'll add a second box limb at the front of the sphere using the lines:
Notice that the mesh used to create the first limb is also used to create the second
limb.
Activity 35.9
Add the above lines to your program and check out the position of the two
limbs.
In the diagram:
The next lines create a third box limb on the sphere and place that limb on the base
of the sphere:
FIG-35.12
A Rotated Limb is Added
to the Sphere
Activity 35.10
Modify your last program so that a total of 6 limbs are added to the sphere
creating a cube around the sphere.
Remove the code which moves the model and allows the camera to be
controlled by the user.
Replace it with code which makes the model rotate about all three of its axes.
In the diagram:
Activity 35.11
In your previous program, modify the width (x) and height (y) of limb 4 to be
200% of their original size; do not modify the depth of the limb.
Modify the program again to make limb 4 grow slowly in width and height
from 100% to 200% and then back again. This action should happen
continuously.
We have ended up with a cube! But surely it would have been much easier to use
the MAKE OBJECT CUBE statement. However, the advantage of using limbs is
that we can now colour or texture the limbs individually.
In the diagram:
For example, limb 1 of object 1 could be assigned the colour yellow using the line
Activity 35.12
Modify your last program so that the cube's sides are coloured as follows:
Side Colour
1 yellow
2 red
3 blue
4 magenta
5 cyan
6 green
In the diagram:
For example, limb 1 of object 1 could be textured using the image onespot.bmp
using the lines:
By using a set of six images, we can create the dice shown in FIG-35.16.
FIG-35.16
Creating a Dice
Images Used
FUNCTION SetUpScreen()
REM *** Set up screen ***
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(0,200,0)
BACKDROP ON
REM *** Position camera ***
AUTOCAM OFF
POSITION CAMERA 0,20,-100
POINT CAMERA 0,0,0
ENDFUNCTION
FUNCTION CreateDiceShape()
REM *** Make and position sphere ***
MAKE OBJECT SPHERE 1,10
MAKE OBJECT BOX 2,10,10,0.1
REM *** Make mesh from object ***
MAKE MESH FROM OBJECT 1,2
REM *** Delete original object ***
DELETE OBJECT 2
REM *** Attach back ***
ADD LIMB 1,1,1
OFFSET LIMB 1,1,0,0,5
REM *** Add front ***
ADD LIMB 1,2,1
OFFSET LIMB 1,2,0,0,-5
REM *** Add bottom ***
ADD LIMB 1,3,1
ROTATE LIMB 1,3,90,0,0
OFFSET LIMB 1,3,0,-5,0
REM *** Add top ***
ADD LIMB 1,4,1
ROTATE LIMB 1,4,90,0,0
OFFSET LIMB 1,4,0,5,0
REM *** Add left side ***
ADD LIMB 1,5,1
ROTATE LIMB 1,5,0,90,0
OFFSET LIMB 1,5,-5,0,0
REM *** Add right side ***
ADD LIMB 1,6,1
continued on next page
FUNCTION RotateDice()
DO
PITCH OBJECT UP 1, 1
TURN OBJECT LEFT 1,1
ROLL OBJECT LEFT 1,1
WAIT 10
LOOP
ENDFUNCTION
Activity 35.13
Activity 35.14
Create a new project (limbsvideo.dbpro). Create a cube using a sphere and six
box limbs. Colour all limbs blue. Load the video vd.mpg on limb 2 of the cube
and make the cube rotate about all three of its axes.
In the diagram:
For example, we could make the single spot on limb 1 of our dice look like four
spots by duplicating the texture twice along both axes. This would require the
statement:
Activity 35.15
Add a SCALE LIMB TEXTURE statement to the main section so that the
single spot looks like six spots.
Activity 35.16
The main section should allow the user to move the camera using the arrow
keys.
In the diagram:
Notice that the root object (the sphere) becomes visible when we remove the side
panel. However, we can avoid this by giving the root object a diameter of zero. This
won't affect the size or visibility of the limbs attached to the sphere.
Activity 35.18
Modify your last program so that the sphere is created with a diameter of zero.
Activity 35.19
In limbs04.dbpro, modify the main section so that limb 1 of the dice reappears
after a key press.
The cone limb is known as the child limb, while the limb to which it is joined (the
cylinder) is known as the parent limb. This effect is achieved using the LINK LIMB
statement which has the format shown in FIG-35.24.
FIG-35.24
The LINK LIMB
LINK LIMB objno , parentlimb , childlimb
Statement
In the diagram:
Start by creating the root object: Create and scale the objects to be Convert objects to meshes:
MAKE OBJECT SPHERE 1,8 used as limbs: MAKE MESH FROM OBJECT 1,2
MAKE OBJECT CYLINDER 2,10 MAKE MESH FROM OBJECT 2,3
SCALE OBJECT 2, 20,300,20 and delete the original objects:
MAKE OBJECT CONE 3,5 DELETE OBJECT 2
DELETE OBJECT 3
The cone is
The cone is attached to the
hidden within centre of the
the sphere cylinder
Add the meshes as limbs of the Link the cone as a child of the cylinder: Move the cone to the end of the
sphere: LINK LIMB 1,1,2 cylinder:
ADD LIMB 1,1,1 OFFSET LIMB 1,2,0,15,0
ADD LIMB 1,2,2
Once attached to a parent limb, a child limb will move, rotate or resize automatically
when the parent limb is altered. The program in LISTING-35.5 demonstrates the
use of a child limb, and shows how that child limb is affected when the parent limb
is moved.
LISTING-35.5 REM *** Main program ***
SetUpScreen()
Using Child Limbs CreateModel()
ManipulateModel()
REM *** End program ***
WAIT KEY
END
FUNCTION SetUpScreen()
REM *** Set up screen ***
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(120,67,39)
BACKDROP ON
REM *** Set up camera ***
AUTOCAM OFF
POSITION CAMERA 0,0,-100
POINT CAMERA 0,0,0
ENDFUNCTION
FUNCTION CreateModel()
REM *** Make model root ***
MAKE OBJECT SPHERE 1,8
REM *** Create objects used as limbs ***
MAKE OBJECT CYLINDER 2,10
SCALE OBJECT 2, 20,300,20
MAKE OBJECT CONE 3,5
REM *** Convert limb objects to meshes ***
MAKE MESH FROM OBJECT 1,2
MAKE MESH FROM OBJECT 2,3
REM *** Delete limb objects ***
DELETE OBJECT 2
DELETE OBJECT 3
REM *** Add limbs to root ***
ADD LIMB 1,1,1
ADD LIMB 1,2,2
REM *** Link cone as child of cylinder ***
LINK LIMB 1,1,2
REM *** Move cone to end of cylinder ***
OFFSET LIMB 1,2,0,15,0
ENDFUNCTION
FUNCTION ManipulateModel()
REM *** Move cylinder up ***
WAIT KEY
FOR y = 1 TO 15
OFFSET LIMB 1,1,0,y,0
WAIT 100
NEXT y
REM *** Rotate whole model ***
WAIT KEY
FOR angle = 0 TO 45
ROLL OBJECT LEFT 1,1
WAIT 100
NEXT angle
ENDFUNCTION
Find out what happens to the cone when the cylinder is deleted, then return the
program to its previous state.
To change the cone in the model created in the last program to a sphere, we would
use the lines:
Activity 35.21
Modify the main section of your last program to change the cone to a sphere.
Activate the change with a key press from the user.
In the diagram:
FIG-35.28 shows the effect of the mode parameter when a cube is glued to the cone
of the model we created earlier.
FIG-35.28
The Effects of the mode
Parameter of the GLUE
OBJECT TO LIMB Object 4 is displaced Object 4 is placed at
The cone has been 15 units along the y-axis
Statement the centre of the limb
displaced 15 units - just as the limb to which to which it is attached
along the y-axis it is attached
Initial set-up. GLUE OBJECT TO LIMB 4,1,2,0 GLUE OBJECT TO LIMB 4,1,2,2
We'll see the GLUE OBJECT TO LIMB statement in action by adding a new
routine, DemoGlue(), to the last program. This new routine creates a cube object,
moves the cylinder limb (and hence the cone limb) over to the cube before attaching
the cube to the cone. After a key press, the whole model is rotated back to its starting
position, with the cube following the movement. The new function is coded as:
FUNCTION DemoGlue()
REM *** Create and position cube ***
MAKE OBJECT CUBE 4,3
POSITION OBJECT 4,30,0,0
Add the following lines - which move the cylinder - to the end of the
CreateModel() function:
ManipulateModel()
to
DemoGlue()
Run the new version of the program. What happens when the cube is linked to
the cone?
Modify the GLUE OBJECT TO LIMB statement to use mode 1 and then
mode 2. How does each change affect the result?
We can use POSITION OBJECT to reposition the glued object relative to the limb
to which it is joined - but we need to take into account the rotation of the cube's
axes. For example, rather than have the cube placed within the centre of the cone
(as you observed in the last Activity) we could make the cube appear to be "glued"
to the cone's surface by moving the cube slightly (see FIG-35.29).
FIG-35.29
Moving the Object after
Glueing
The Cube Attached to the Centre of the Cone The Cube Repositioned Slightly after Attachment
From the moment an object is glued to a limb, the object's new position becomes
its new origin (0,0,0) and the axes are orientated with those of the limb to which it
is attached. For our cube, the consequences are shown in FIG-35.30.
So to move the cube down slightly (as shown in FIG-35.29) we need to use the line:
Activity 35.23
+y
-y
(0,0,0)
+x
The UNGLUE OBJECT Statement
To detach an object from a group model, we need to use the UNGLUE OBJECT
statement which has the format shown in FIG-35.31.
FIG-35.31
UNGLUE OBJECT objno
The UNGLUE OBJECT
Statement
In the diagram:
In our previous program we could unglue the cube from the cone using the line:
UNGLUE OBJECT 4
Activity 35.24
Modify function DemoGlue() in your last program so that the cylinder swings
back down to 85o, unglues the cube, and then swings back up to the vertical
position. Use a key press to activate each operation. How is the cube affected
when it is unglued from the cone?
Activity 35.25
Creating Doors
Most doors are hinged on one side and open by rotating about the hinged edge.
When we need to create a door in a game, it is simple enough to create a thin box
with the appropriate texture (see FIG-35.33), but it takes a bit more effort to get the
door to swing open. Using ROTATE OBJECT won't work because the door will
rotate about its centre rather than its edge.
FIG-35.33
Creating a Door
To create a door that will swing open we need to resort to making the box a limb.
We can then open the door by rotating the root object. The necessary code is shown
in LISTING-35.6.
LISTING-35.6 SetupScreen()
CreateDoor()
Opening a Hinged Door WAIT KEY
SwingDoor(80)
WAIT KEY
END
FUNCTION SetUpScreen()
REM *** Set screen resolution and position camera ***
SET DISPLAY MODE 1280, 1024,32
COLOR BACKDROP 0
FUNCTION CreateDoor()
#CONSTANT doorimg 1
#CONSTANT doorhingeobj 1
#CONSTANT doorobj 2
#CONSTANT doormesh 1
#CONSTANT doorlimb 1
FUNCTION SwingDoor(angle)
REM *** Swing door 1 degree at a time ***
FOR c = 1 TO angle
ROTATE OBJECT doorhingeobj,0,c,0
WAIT 50
NEXT c
ENDFUNCTION
Activity 35.26
Modify the SwingDoor() routine so that the door swings from its current
starting place and allow the delay between each 1o move to be specified as a
parameter.
integer
integer
In the diagram:
The current offset values of a limb can be determined using the LIMB OFFSET
statement which has the format shown in FIG-35.36.
FIG-35.36 X
The LIMB OFFSET
Statement LIMB OFFSET Y ( objno , limbno )
real
If a limb is attached to the root object, then the values returned by this statement
relate to the limb centre's displacement from the root object's centre. If a limb is a
child limb then the values returned are measured from the parent limb's centre to
the child limb's centre.
Activity 35.27
Modify your last version of limbs05.dpro to display the offset values in each
direction of both the cylinder and the cone.
real
In the diagram:
For example, we could determine the scaling that has been applied to the cylinder
(limb 1) in our previous program using the lines:
real
In the diagram:
real
In the diagram:
real
In the diagram:
FIG-35.41 shows a screen dump from the program in LISTING-35.7 which displays
all the limb details for the cone in our previous model as it carries out various actions.
FIG-35.41
Displaying Limb Details
FUNCTION HandleUser()
REM *** Display options ***
SET CURSOR 0,100
PRINT "1 - Rotate arm"
PRINT "2 - Rotate cone"
PRINT "3 - Move model"
PRINT "4 - Scale cone"
PRINT "5 - QUIT"
SYNC
REM *** Get user's choice ***
REPEAT
key$ = INKEY$()
UNTIL key$ >= "1" AND key$ <= "5"
REM *** Execute option chosen ***
SELECT key$
CASE "1"
RotateArm()
WAIT KEY
ENDCASE
CASE "2"
RotateCone()
WAIT KEY
ENDCASE
CASE "3"
POSITION OBJECT 1,OBJECT POSITION X(1)-5, 0,0
WAIT KEY
ENDCASE
CASE "4"
ScaleCone()
WAIT KEY
ENDCASE
ENDSELECT
ENDFUNCTION
FUNCTION SetUpScreen()
REM *** Set up screen ***
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(120,67,39)
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,0,-100
POINT CAMERA 0,0,0
SYNC ON
ENDFUNCTION
FUNCTION CreateModel()
REM *** Make model root ***
MAKE OBJECT SPHERE 1,8
REM *** Create objects used as limbs ***
MAKE OBJECT CYLINDER 2,10
SCALE OBJECT 2, 20,300,20
MAKE OBJECT CONE 3,5
REM *** Convert limb objects to meshes ***
MAKE MESH FROM OBJECT 1,2
MAKE MESH FROM OBJECT 2,3
REM *** Delete limb objects ***
DELETE OBJECT 2
DELETE OBJECT 3
continued on next page
FUNCTION RotateCone()
REM *** Rotate cone 1 degree at a time ***
FOR angle = 1 TO 90
ROTATE LIMB 1,2,0,0,angle
REM *** Show limb data ***
DisplayLimbData(1,2)
SYNC
WAIT 50
NEXT angle
ENDFUNCTION
FUNCTION ScaleCone()
REM *** Scale the cone 1% at a time ***
FOR scale = 100 TO 50 STEP -1
SCALE LIMB 1,2,100,scale,100
REM *** Display limb data ***
DisplayLimbData(1,2)
SYNC
WAIT 50
NEXT scale
ENDFUNCTION
FUNCTION RotateArm()
REM *** Rotate model showing limb data ***
FOR angle = 0 TO 90
ROLL OBJECT RIGHT 1,1
DisplayLimbData(1,2)
SYNC
WAIT 50
NEXT angle
WAIT KEY
REM *** Rotate model back to start position ***
FOR angle = 0 TO 90
ROLL OBJECT LEFT 1,1
DisplayLimbData(1,2)
SYNC
WAIT 50
NEXT angle
ENDFUNCTION
FUNCTION DisplayLimbData(objno,limbno)
REM *** Get limb data ***
xoffset# = LIMB OFFSET X(objno,limbno)
yoffset# = LIMB OFFSET Y(objno,limbno)
zoffset# = LIMB OFFSET Z(objno,limbno)
xangle# = LIMB ANGLE X(objno,limbno)
yangle# = LIMB ANGLE Y(objno,limbno)
zangle# = LIMB ANGLE Z(objno,limbno)
xpost# = LIMB POSITION X(objno,limbno)
ypost# = LIMB POSITION Y(objno,limbno)
zpost# = LIMB POSITION Z(objno,limbno)
xdir# = LIMB DIRECTION X(objno,limbno)
ydir# = LIMB DIRECTION Y(objno,limbno)
zdir# = LIMB DIRECTION Z(objno,limbno)
xscale# = LIMB SCALE X(objno,limbno)
yscale# = LIMB SCALE Y(objno,limbno)
continued on next page
FUNCTION Format$(v#,totaldigits,decplaces)
REM *** Check number's sign ***
The Format$() routine is IF v# < 0
described in the May 2006 sign = -1
Support page of our ELSE
website. sign = 1
ENDIF
REM *** Round number ***
v# = sign*ABS(v#+0.5/10^decplaces)
REM *** Convert number to string ***
v$ = STR$(v#)
REM *** Find position of decimal point ***
FOR size = 1 TO LEN(v$)
IF MID$(v$,size) = "."
EXIT
ENDIF
NEXT size
REM *** Extract required decimal places from string ***
result$ = LEFT$(v$,size+decplaces)
REM *** Pad string to size required ***
WHILE LEN(result$) < totaldigits
result$ = " "+result$
ENDWHILE
ENDFUNCTION result$
Activity 35.28
For example, we could create a checklist of the limbs for a model whose root object
had the ID 5 using the line:
The checklist produced contains details of the root object and all limbs added to
that object.
To access the checklist created by this command we need to use the following
statements:
These statements were CHECKLIST QUANTITY
covered in pages 364 - CHECKLIST STRING$
366.
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 0,0,-100
POINT CAMERA 0,0,0
SYNC ON
ENDFUNCTION
FUNCTION ModelDetails()
PERFORM CHECKLIST FOR OBJECT LIMBS 1
SET CURSOR 0,100
PRINT "This object contains ", CHECKLIST QUANTITY(),
Ä" components"
FOR c = 1 TO CHECKLIST QUANTITY()
PRINT CHECKLIST STRING$(c)
NEXT c
SYNC
ENDFUNCTION
Activity 35.29
string
In the diagram:
Activity 35.30
Modify the previous program so that the function ModelDetails() contains the
lines:
FOR c = 1 TO 2
PRINT LIMB NAME$(1,c)
NEXT c
SYNC
integer
In the diagram:
For example, the texture ID for limb 2 of model 1 can be found using a line such
as:
string
If no name is assigned to the texture, or no texture has been applied to the limb, then
an empty string is returned.
For example, the texture name for limb 2 of model 1 can be found using a line such
as:
integer
In the diagram:
The statement returns a value of 1 if a link to the limb specified by limbno is legal;
otherwise zero is returned.
For example, we could check that a link to limb number 2 in model 1 was possible
with code such as:
IF CHECK LIMB LINK(1,2) = 1
PRINT "Link to limb 2 is valid"
ENDIF
The model is made from a zero-sized sphere as the root object, with a cylinder and
a box as limbs.
FUNCTION ScreenSetUp()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(200,200,80)
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,5,-120
POINT CAMERA 0,0,0
ENDFUNCTION
FUNCTION MakeElevator()
REM *** Create Objects required ***
MAKE OBJECT SPHERE 1,0
MAKE OBJECT CYLINDER 2,100
SCALE OBJECT 2,0.5,100,0.5
MAKE OBJECT BOX 3,5,0.2,5
REM *** Create limb meshes ***
MAKE MESH FROM OBJECT 1,2
MAKE MESH FROM OBJECT 2,3
Activity 35.31
For example, to save the elevator model created in the previous program, we would
use the line:
Activity 35.32
Replace the call to RaiseElevator() in the main section of your last program
with the line given above and check that the file elevator.dbo is created.
In the diagram:
For example, we could load the elevator model using the line:
The above line assumes that the file elevator.dbo is in the current project's folder.
In fact, this is the simplest form of the LOAD OBJECT statement. We'll leave a full
explanation of the statement until the next chapter.
FUNCTION ScreenSetUp()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(200,200,80)
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,5,-20
POINT CAMERA 0,0,0
ENDFUNCTION
Activity 35.33
If a model uses textures, the images employed must also be copied to the same folder
Activity 35.34
Copy the steel.bmp and metal.bmp files used by the elevator model into the
limbs10 folder.
Add the RaiseElevator() routine to your program and check that the platform
moves in the same way as before.
Summary
l A 3D object which is constructed from several component parts is known as a
group object or model.
l The root object may have zero width, height and depth.
l Colouring or texturing the root object will result in the limbs of the model taking
on the same colour or texture.
l Meshes are linked to the root object and become limbs within the model.
l Limbs are initially positioned with their centre at the centre of the root object.
l Use MAKE OBJECT FROM LIMB to create a normal 3D object from a limb
within a model. The limb is unaffected by this operation.
l Use CHANGE MESH to change which mesh is used for an existing limb.
l Use LIMB OFFSET to discover the current offset settings for a specific limb.
l Use LIMB SCALE to discover the current scaling factors for a specific limb.
l Use LIMB ANGLE to discover a limb's rotation relative to its parent limb or the
root.
l Use LIMB POSITION to discover a limb's position relative to the world axes.
l Use LIMB DIRECTION to discover a limb's rotation relative to the world axes.
l The root object is included in the list created by the PERFORM CHECKLIST
FOR OBJECT LIMBS.
l Use LIMB TEXTURE to access the image ID of the texture used on a specific
limb.
l Use LIMB TEXTURE NAME to access the name assigned to the texture used
on a specific limb.
l Use CHECK LIMB LINK to check if a specific limb can be the parent of a new
limb.
No solution required.
Activity 35.7
Activity 35.5 REM *** Set up screen ***
SET DISPLAY MODE 1280,1024,32
REM *** Set up screen *** COLOR BACKDROP 0
SET DISPLAY MODE 1280,1024,32 BACKDROP ON
COLOR BACKDROP 0 REM *** Position camera ***
BACKDROP ON AUTOCAM OFF
REM *** Position camera *** POSITION CAMERA 0,10,-100
AUTOCAM OFF POINT CAMERA 0,0,0
POSITION CAMERA 0,10,-100 REM *** Make and position sphere ***
POINT CAMERA 0,0,0 MAKE OBJECT SPHERE 1,10
REM *** Make and position sphere *** MAKE OBJECT BOX 2,10,10,0.1
MAKE OBJECT SPHERE 1,10 REM *** Make mesh from object ***
FUNCTION CreateModel()
REM *** Declare named constants ***
REM *** Images ***
#CONSTANT floorimg 1
#CONSTANT ceilingimg 2
#CONSTANT wallimg 3
REM *** Objects ***
FUNCTION CreateModel()
REM *** Make model root ***
MAKE OBJECT SPHERE 1,8
REM *** Create objects used as limbs ***
MAKE OBJECT CYLINDER 2,10
Activity 35.19 SCALE OBJECT 2, 20,300,20
MAKE OBJECT CONE 3,5
REM *** Main program *** REM *** Convert objects to meshes ***
SetUpScreen() MAKE MESH FROM OBJECT 1,2
CreateDiceShape() MAKE MESH FROM OBJECT 2,3
TextureDice() REM *** Delete limb objects ***
RotateDice() DELETE OBJECT 2
HIDE LIMB 1,1 DELETE OBJECT 3
REM *** Make limb reappear *** REM *** Add limbs to root ***
WAIT KEY ADD LIMB 1,1,1
SHOW LIMB 1,1 ADD LIMB 1,2,2
REM *** End program *** REM *** Make cone child of cylinder ***
WAIT KEY LINK LIMB 1,1,2
END REM *** Move cone to end of cylinder ***
OFFSET LIMB 1,2,0,15,0
REM *** Position cylinder ***
Activity 35.20 OFFSET LIMB 1,1,0,15,0
ENDFUNCTION
Modify the main section to
FUNCTION DemoGlue()
REM *** Main program *** REM *** Create and position cube ***
SetUpScreen() MAKE OBJECT CUBE 4,3
CreateModel() POSITION OBJECT 4,30,0,0
ManipulateModel() REM *** Rotate model to cube ***
REM *** Remove cylinder *** FOR angle = 0 TO 85
WAIT KEY ROLL OBJECT RIGHT 1,1
REMOVE LIMB 1,1 WAIT 50
REM *** End program *** NEXT angle
WAIT KEY REM *** Glue cube to cone ***
END GLUE OBJECT TO LIMB 4,1,2,0
WAIT KEY
Since the cone is attached to the cylinder, it is also REM *** Rotate model upright ***
FOR angle = 0 TO 85
removed from the model. ROLL OBJECT LEFT 1,1
WAIT 50
NEXT angle
Activity 35.21 ENDFUNCTION
Modify the main section to:
When the program runs, the cube takes on the same offset
REM *** Main program *** and rotational values as the cone.
SetUpScreen()
CreateModel() A mode value of 1 gives the same effect, but with mode
ManipulateModel() set to 2, the centre of the cube is linked to the centre of the
REM *** Change cone to sphere *** cone.
WAIT KEY
MAKE OBJECT SPHERE 2,5
MAKE MESH FROM OBJECT 3,2
CHANGE MESH 1,2,3
REM *** End program ***
WAIT KEY
END
Activity 35.30
The updated version of Modeldetails() should be:
FUNCTION ModelDetails()
PERFORM CHECKLIST FOR OBJECT LIMBS 1
PRINT "This object contains ",
CHECKLIST QUANTITY()," components"
FOR c = 1 TO CHECKLIST QUANTITY()
PRINT CHECKLIST STRING$(c)
NEXT c
SYNC
FOR c = 1 To 2
PRINT LIMB NAME$(1,c)
NEXT c
SYNC
ENDFUNCTION
Activity 35.31
No solution required.
Activity 35.32
The main section of your program should now be as
follows:
ScreenSetUp()
MakeElevator()
SAVE OBJECT "elevator.dbo",1
WAIT KEY
END
Activity 35.33
The model is not textured.
Activity 35.34
The model is correctly textured when the required images
are placed in the project folder.
ScreenSetUp()
LOAD OBJECT "elevator.dbo",1
RaiseElevator()
WAIT KEY
END
FUNCTION ScreenSetUp()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(200,200,80)
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,5,-120
POINT CAMERA 0,0,0
ENDFUNCTION
Realistically, we need to create these complex shapes in some other package - and
there are many packages out there which are designed specifically to do just that.
If you've already made a few million from selling your DarkBASIC Pro game, then
you can afford professional-level 3D creation software such as Maya, 3D Studio
Max, or XSI SoftImage. For those with a more limited budget, have a look at some
of the packages available from The Game Creators such as 3D Canvas Pro or
gameSpace. There are even free or shareware packages such as Anim8or and
Milkshape.
Don't worry if you have absolutely no artistic ability (like me!), you'll still be able
to create models with 3D modelling software that will truly amaze you; if you do
have an artistic side, then you will be able to produce just about anything you can
imagine.
One word of warning, when creating 3D models that are to be used in a game, you
need to keep the number of polygons used as low as possible. The more polygons
an object contains, the more load you place on the processor and graphics card. With
more processing to do, the game's frame rate will drop, and when it gets too low,
your game will become jerky and unconvincing.
As usual, there is software out there to help overcome even this problem.
Polygon-reducing software attempts to reduce the number of polygons a 3D object
uses, while still retaining as much detail as possible.
Of course, learning how to use a 3D modelling package takes time - a long time if
you want to be really proficient - so, as an alternative, you can buy ready-to-use 3D
models such as the Dark Matter series offered by The Game Creators (see FIG-36.1).
FIG-36.1
A 3D Model from the
Dark Matter CD
File Formats
Almost every 3D modelling package has its own format for saving objects to a file.
DarkBASIC Pro accepts several formats, BSP, MD2, MD3, MDL, PAK , but the
commonest are Microsoft's DirectX format (these files end with a .x extension) and
3D Studio format (.3ds extension) as well as DarkBASIC Pro's own .dbo format.
The examples given in this chapter make use of files available from Dark Matter
Volume 1.
In the diagram:
Activity 36.1
Although the gun is the correct shape, it doesn't have any texturing. The weapon
has been designed with a texture, but this is held in a separate file called AK47.DDS.
If we want the texture to appear on the gun, we must copy the .dds file to the current
program folder.
Activity 36.2
By clicking on DarkBASIC Pro's Media button and selecting Add, copy the
AK47.DDS file into the loaded01 folder created by the last program.
Re-run your program. (The gun should now be displaying its texture.)
Activity 36.3
Modify your last program to load and rotate the jet fighter held in file H-Jet
Fighter-Move.x. The associated textures are held in JetFighter.dds.
Once loaded, a 3D object can be manipulated using all the standard OBJECT
statements, thus allowing us to do such things as position, rotate and resize objects.
Not all 3D objects are static, unmoving things. Animated 3D objects will actually
be held as a series of frames; a bit like an animated sprite. For example, Dark Matter's
Hive-Brain alien comes in several files, each of these contain a few frames showing
the alien performing some basic movement.
Activity 36.4
Despite being an animated model, the alien shows no sign of any movement.
In the diagram:
If start and finish are omitted, the whole animation is played. If finish is omitted
every frame from start to the end of the animation is played.
For example,
PLAY OBJECT 1
In the diagram:
If start and finish are omitted, the whole animation is played. If finish is omitted
every frame from start to the end of the animation is played.
Activity 36.6
To create a smooth animation, the first and last frames need to be almost identical.
The walking alien judders slightly as it cycles back to the first frame. We'll improve
this in a moment.
integer
Activity 36.7
Add the appropriate code to your program so that the number of frames in the
alien animation is displayed on the screen.
Now that we know just how many frames are in the animated object, we can limit
exactly which frames are played.
Activity 36.8
Modify the LOOP OBJECT statement in your program so that only frames 0
to 20 are used in the animation shown on the screen.
Activity 36.9
Activity 36.10
In the diagram:
When stopped, the model will continue to show the last frame played. This could
be any frame within the animation.
Activity 36.11
Modify your program so that the hive alien's animation (and repositioning) is
halted when it moves to a z-ordinate value of less than -6.
In the diagram:
boundsflg 0 or 1.
If, when we move to a different frame in an animated 3D object, that object has
changed orientation, then we have the option to reposition the bounding volume to
match the new position of the object (see FIG-36.9).
FIG-36.9 Recalculating the Bounding Volume for a New Frame
Every 3D object is surrounded by If the object is animated and moved to a However, if boundsflg = 0, then the
an invisible bounding volume used to new frame, using boundsflg = 1 will move bounding volume will not be
help detect collisions. the bounding volume with the object. repositioned.
Failing to recalculate the bounding volume will give us inaccurate collision results.
Activity 36.12
Modify your program so that when the hive alien stops, frame 13 is shown.
Activity 36.13
To add the alien attack animation to the end of the object containing the moving
alien (object 1), we'd use the line:
Activity 36.14
integer
In the diagram:
The statement returns 1 if the specified object is playing; otherwise zero is returned.
FIG-36.13
OBJECT LOOPING ( objno )
The OBJECT
LOOPING Statement
integer
In the diagram:
integer
In the diagram:
The function returns the number of the frame currently being displayed by the
specified object.
FIG-36.15
OBJECT SPEED ( objno )
The OBJECT SPEED
Statement
integer
In the diagram:
The statement returns the current speed setting. The default speed of a 3D animated
object is 100, and that will be the value returned by this statement, unless the object
in question has had its speed changed using the SET OBJECT SPEED.
integer
real
In the diagram:
For example, we could display the overall size of object 1 using the line:
The second version of OBJECT SIZE is used to discover the actual size of the 3D
object in each dimension. This version of the statement has the format shown in
FIG-36.18.
FIG-36.18
X
The OBJECT SIZE
Statement (Version 2)
OBJECT SIZE Y ( objno )
real
In the diagram:
For example, we could discover the height of object 1 using the line:
PRINT OBJECT SIZE Y(1)
Activity 36.15
Limbs
We looked at limbs in the previous chapter, but here we have a chance to examine
the limbs of a model created outwith DarkBASIC Pro.
The program in LISTING-36.6 displays the names of all the limbs in the model
H-Alien Hivebrain-Move.x and then hides one of the limbs.
LISTING-36.6 REM *** Set up screen ***
SET DISPLAY MODE 1280, 1024, 32
Examining the Limbs of COLOR BACKDROP 0
an Imported Model BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,0,-10
POINT CAMERA 0,0,0
SYNC ON
The limbs of an imported model can be manipulated using the statements covered
in the previous chapter.
Activity 36.17
Modify your last program to hide limb 2 from the Hivebrain model.
Summary
l Complex 3D models are usually created using a 3D drawing package.
l DarkBASIC Pro can import .DBO, .3DS, .X and other format models.
l Animated models consist of several frames - a bit like frames from a movie.
l Each frame of an animated model shows the model in a slightly different pose
from the previous frame.
l Make sure the model and the texturing it uses are stored in the current project
folder.
l Use TOTAL OBJECT FRAMES to discover exactly how many frames are used
in an animated model.
l Use SET OBJECT SPEED to set the playing speed of a playing model animation.
l Use APPEND OBJECT to add the frames of one model to those of another.
l Use OBJECT SPEED to discover the current speed setting for an animated
model.
l The limbs of an imported model can be manipulated in the same way as limbs
created using DarkBASIC Pro code.
Activity 36.13
No solution required.
Activity 36.14
In this chapter we're going to look at some techniques for helping the player to select
from a group of 3D objects, as well as finding out the commands available to help
with this task.
Selecting an Object
FIG-37.1 shows a line of three balls. The aim of this section is to examine ways of
selecting one of the three balls for deletion.
FIG-37.1
A Choice of Objects
FUNCTION CreateBalls()
#CONSTANT firstballobj 11
#CONSTANT lastballobj 13
#CONSTANT ballimg 2
Type in the code given above (control01.dbpro) and add a main section to call
these functions and display the balls on the screen.
Next, we will add a selector. This would give the player a visual clue as to which
object is currently selected (see FIG-37.2).
FIG-37.2
Adding a Selector
The new object is simply a cube textured with a blue square and a black centre (see
FIG-37.3). The black area of the object has been made transparent.
FIG-37.3
The Texture Used on
the Selector Cube
FUNCTION CreateSelector()
#CONSTANT selectorobj 2
#CONSTANT selectorimg 3
Activity 37.2
Add the new function to your program (calling it from the main section) and
check that the selector is created successfully.
The next function allows the user to move the selector left and right using the arrow
keys and to make a final selection by pressing the Enter key.
FUNCTION MoveSelector()
REPEAT
REM *** Get current selector position ***
x = OBJECT POSITION X(selectorobj)
y = OBJECT POSITION Y(selectorobj)
The WAIT 100 statement at the end of the function is there to slow down the whole
process. If not included the selector is too responsive to key presses.
Activity 37.3
Add the new function to your program and test that it works correctly.
When the selector has moved over the required ball, we can see on the screen which
item is selected, but we also need to get the program itself to realise which ball has
been picked. To do that we need to learn a few more DarkBASIC Pro statements.
Screen 1 Screen 2
FIG-37.4
Screen Position Changes
though 3D Position is
Unchanged
The objects shift to a different position on the screen as the camera is moved
but remain fixed at the same location in world space.
We can use the OBJECT SCREEN statement to discover the x,y coordinates of a
3D object on the screen. The statement has the format shown in FIG-37.5
{
FIG-37.5
X
The OBJECT SCREEN OBJECT SCREEN ( objno )
Statement
Y
real
In the diagram:
For example, we could discover the screen coordinates of the selector object in the
program we are developing using the lines:
integer
In the diagram:
If any part of an object with an ID between objno1 and objno2 is positioned at screen
coordinates (x,y), then the ID of that object is returned by the statement. If point
(x,y) does not lie within any of the objects, zero is returned.
For example, we could check which ball covers the screen position (selectorx#,
selectory#) with the line:
objno =
PICK OBJECT(selectorx#,selectory#,firstballobj,lastballobj)
FUNCTION GetObjectSelected()
selectorx# = OBJECT SCREEN X(selectorobj)
selectory# = OBJECT SCREEN Y(selectorobj)
objno =
PICK OBJECT(selectorx#,selectory#,firstballobj,lastballobj)
ENDFUNCTION objno
To finish off, we'll create a routine to handle the ball selected. For the moment we'll
just make the ball disappear.
Activity 37.4
ScreenSetUp()
CreateBalls()
CreateSelector()
DO
MoveSelector()
objno = GetObjectSelected()
HandleObject(objno)
LOOP
WAIT KEY
END
We can access this information using the GET PICK DISTANCE statement which
has the format shown in FIG-37.8.
FIG-37.8
The GET PICK GET PICK DISTANCE ( )
DISTANCE Statement
real
The statement returns the distance in world units between the camera and the surface
of the object returned by the previous PICK OBJECT statement.
We could rewrite the HandleObject() routine to display the distance of the selected
sphere:
FUNCTION HandleObject(objno)
REM *** IF no object selected, exit ***
IF objno = 0
EXITFUNCTION
ENDIF
REM *** Find distance from camera to ball ***
distance# = GET PICK DISTANCE()
INK 0,0
REM *** Display distance for 2 seconds ***
t = TIMER()
WHILE TIMER() - t < 2000
SET CURSOR 100,100
PRINT "Distance = ",distance#
ENDWHILE
ENDFUNCTION
Activity 37.5
Test the routine by checking the distance of all three balls from the camera.
We can examine the contents of this vector using the PICK VECTOR statement
which is shown in FIG-37.10.
FIG-37.10
{
X
The PICK VECTOR
Statement
PICK VECTOR Y ( )
real
In the diagram:
returns a non-zero value, we could get the returned object's position (relative to the
camera) using the lines:
The program in LISTING-37.2 allows the user to move the camera using the arrow
keys and displays a continual readout of the relative position of a sphere in relation
to the camera.
FUNCTION DisplayCoords(objno)
REM *** Get object's relative coords ***
x# = GET PICK VECTOR X()
y# = GET PICK VECTOR Y()
z# = GET PICK VECTOR Z()
REM *** Display info on screen ***
SET CURSOR 100,100
PRINT "Object:",objno,"Relative coords x:",x#," Y:",y#,
" z:",z#
ENDFUNCTION
Activity 37.6
The operation is performed using the PICK SCREEN statement which has the
format shown in FIG-37.11.
FIG-37.11
The PICK SCREEN PICK SCREEN x , y , distance
Statement
the computer would work out the equivalent point in the 3D world and return the
coordinates of that point relative to the active camera's position.
The program in LISTING-37.3 allows the user to mouse click anywhere on the
screen and returns the coordinates of the equivalent point at a screen depth of 15
units.
LISTING-37.3 REM *** Set up screen ***
SET DISPLAY MODE 1280,1024,32
Screen Coordinates AUTOCAM OFF
Converted to World POSITION CAMERA 0,0,-10
Coordinates POINT CAMERA 0,0,0
REM *** Create object to create correct screen mode ***
MAKE OBJECT SPHERE 1,0
REM *** main loop ***
DO
DisplayCoords()
LOOP
END
FUNCTION DisplayCoords()
REM *** Get mouse coords ***
PICK SCREEN MOUSEX(), MOUSEY(), 15
x# = GET PICK VECTOR X()
y# = GET PICK VECTOR Y()
z# = GET PICK VECTOR Z()
REM *** Display info on screen ***
SET CURSOR 100,100
PRINT "Relative coords x:",x#," Y:",y#," z:",z#
ENDFUNCTION
Activity 37.7
integer
The routine returns 1 if the object specified is wholly or partially on the screen;
otherwise zero is returned.
FUNCTION DisplayStatus(objno)
REM *** Display info on screen ***
SET CURSOR 100,100
IF OBJECT IN SCREEN(objno)
PRINT "On screen"
ELSE
PRINT "Off screen"
ENDIF
ENDFUNCTION
Activity 37.8
FUNCTION CreateBalls()
#CONSTANT firstballobj 11
#CONSTANT lastballobj 13
#CONSTANT ballimg 2
REM *** Load ball texture ***
LOAD IMAGE "lava1.bmp",ballimg
REM *** Create balls ***
x = -9
FOR objno = firstballobj TO lastballobj
MAKE OBJECT SPHERE objno,2,20,20
TEXTURE OBJECT objno,ballimg
POSITION OBJECT objno,x,0,0
x = x + 3
NEXT objno
ENDFUNCTION
FUNCTION GetObjectSelected()
REM *** Wait for mouse click ***
WHILE MOUSECLICK()=0
ENDWHILE
REM *** Select object under mouse pointer ***
objno = PICK OBJECT(MOUSEX(),MOUSEY(),firstballobj,lastballobj)
ENDFUNCTION objno
FUNCTION HandleObject(objno)
REM *** IF no object selected, exit ***
IF objno = 0
EXITFUNCTION
ENDIF
HIDE OBJECT objno
ENDFUNCTION
Activity 37.9
Modify the program to have a 3 by 3 grid of balls. Each new row of balls
should be displaced by 3 units along the z-axis (see below).
l Use the PICK OBJECT statement to discover the ID of an object at a given point
on the screen.
l Use GET PICK DISTANCE to discover how far the object detected by PICK
OBJECT is from the camera.
l Use PICK VECTOR to discover the coordinates of the object detected by PICK
OBJECT relative to the camera.
l Use PICK SCREEN to convert a 2D screen position and a depth value to a point
in 3D space. The coordinates generated are relative to the camera position.
FUNCTION ScreenSetUp()
REM *** Set up screen *** Activity 37.5
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(255,255,100) No solution required.
BACKDROP ON
REM *** Position camera ***
AUTOCAM OFF Activity 37.6
POSITION CAMERA 0,10,-20
POINT CAMERA 0,0,0 No solution required.
ENDFUNCTION
Activity 37.9
To create nine balls we need to modify the CreateBalls()
function to create 3 rows of 3. The new version of the
routine given below keeps changes to a minimum.
FUNCTION CreateBalls()
#CONSTANT firstballobj 11
#CONSTANT lastballobj 19
#CONSTANT ballimg 2
REM *** Load ball texture ***
LOAD IMAGE "lava1.bmp",ballimg
REM *** Create balls ***
x = -9
z = 0
FOR objno = firstballobj TO lastballobj
MAKE OBJECT SPHERE objno,2,20,20
TEXTURE OBJECT objno,ballimg
POSITION OBJECT objno,x,0,z
x = x + 3
IF x = 0
x = -9
z = z + 3
ENDIF
NEXT objno
ENDFUNCTION
FIG-38.1
The Game of Solitaire
The Equipment
The game consists of 32 marbles and a board with 33 indentations, or pits. The pits
form the shape of a plus sign (+). A marble is placed in every pit, except the central
one.
The Aim
The aim of the game is to remove all but one marble from the board. This last marble
should be in the central pit at the end of the game.
The Rules
Any marble can be moved by jumping over one other marble into an empty pit.
Jumps can be horizontal or vertical but not diagonal. Only a single marble can be
jumped over on each move. The marble which is jumped over is removed from the
board.
To achieve this, the player will move a selector cube about the board using the arrow
keys. Once over a required pit, the Enter key is pressed to indicate a selection.
Pressing F1 will display the help screen. Within the help screen, pressing R will
display the rules, and K the keys which may be used in the game.
Game Responses
The game will respond to player actions in the following ways:
Screen Layout
The screen layout is shown in FIG-38.1.
Media Used
The images used to texture the board, marbles and selector cube are shown in
FIG-38.2. Notice that even the help text is held as a single image with three frames.
Marble texture
Data Structures
Although we can use 3D objects to represent the board visually, we need a second
way of representing the board as data. This will make the coding easier when trying
to decide on valid moves.
Now we'll fill the array with a zero in any cell that corresponds to a pit and with -1
where a cell corresponds to a flat part of the board. FIG-38.3 shows the board and
the corresponding array contents.
FIG-38.3
The Board and the Array -1 -1 0 0 0 -1 -1
used to Represent it -1 -1 0 0 0 -1 -1
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
-1 -1 0 0 0 -1 -1
-1 -1 0 0 0 -1 -1
board Array
Actual Board Layout
Lower ID values are Rather than just store zeros, it will suit us better to store the IDs of each marble in
reserved for other objects. the cells of the array. So, if we assume the marbles have object IDs starting at 11,
the board array would start off as shown in FIG-38.4.
FIG-38.4 -1 -1 11 12 13 -1 -1
-1 -1 14 15 16 -1 -1
The board array with 17 18 19 20 21 22 23
Marble IDs
24 25 26 27 28 29 30
31 32 33 34 35 36 37
-1 -1 38 39 40 -1 -1
-1 -1 41 42 43 -1 -1
board Array
When we create a 3D object, its centre is at (0,0,0), so, assuming we don't move the
board, the centre pit will have x, z values of 0,0.
Since we need to store two values in every cell of the array (the x and z ordinates),
we'll need to create a record structure for this
TYPE Coords
x AS INTEGER
z AS INTEGER
ENDTYPE
Now, assuming the centre of each pit is 3 world units in both directions from any
neighbour, the new array would contain the values shown in FIG-38.5.
FIG-38.5
-1 -1 (-3,9) (0,9) (3,9) -1 -1
The boardCoords Array
-1 -1 (-3,6) (0,6) (3,6) -1 -1
(-9,3) (-6,3) (-3,3) (0,3) (3,3) (6,3) (9,3)
We'll also need to record the position of the selector cube, but this time we'll use its
position on the board, not its screen position. For example, if the selector is at the
centre of the board, then it is in row 4, column 4. To store this information, we'll
need another record structure
TYPE BoardPosition
row AS INTEGER
col AS INTEGER
ENDTYPE
selectorposition AS BoardPosition
On each turn the player chooses a marble and a position to which the marble is to
The final important variable that we will require is a game-state variable. There are
three main stages during game play. These are:
Lastly, we need to record how many marbles are currently on the board; when this
reduces to one, the game is over. We will do this using a variable named
marbleremaining.
All the variables above will be made global so that they can be accessed throughout
the program:
TYPE BoardPosition
row AS INTEGER
col AS INTEGER
ENDTYPE
The images and 3D objects also need to be assigned ID values and it will make the
code clearer if these are given names in #CONSTANT statements.
REM **********************************
REM *** Constants ***
REM **********************************
REM *** Object constants ***
#CONSTANT boardobj 1
#CONSTANT selectorobj 2
#CONSTANT firstmarbleobj 3
Now it's time to have a look at the logic of the game. As usual, for a larger project,
the main section should consist mainly of function calls so that we can get an
overview of what's going on in just a few lines. In English, the whole logic could
be described as follows:
Initialise screen
Set up game
REPEAT
Get player's move
UNTIL game over
In the main, this translates into straight function calls but we'll have to know if the
player has chosen to quit the game, so the function which gets the player's move
will have to return a flag to indicate if the game should be ended. This means that
the main section should be coded as:
SetUpScreen()
SetUpGame()
REPEAT
quit = GetPlayersMove()
UNTIL quit OR marblesremaining = 1
END
Activity 38.1
Create a new project (Solitaire.dbpro) and copy the code for the constants and
data into the source file.
Code the main section as given above and add test stubs for each of the
functions called. The test stub for GetPlayersMove() should be:
FUNCTION GetPlayersMove()
PRINT "GetPlayersMove() executed"
ENDFUNCTION 1
To stop the program finishing before you get time to see all the messages
displayed, add a WAIT KEY statement before the END command in the
program's main section.
Make sure all the media files have been copied to the project's folder.
Now we'll begin to build up the project by working our way through each routine
needed.
Adding SetUpScreen()
This routine sets the screen resolution and backdrop as well as positioning the
camera, so its code is quite straight forward:
Activity 38.2
Replace the test stub for SetUpScreen() with the actual code as given above.
Once a 3D object (or sprite) appears on the screen, or we start using a camera, the
screen refresh method employed means that the output from any PRINT statement
vanishes almost as soon as it appears. Because of this, we can no longer see the
messages confirming that routines have been executed.
You might feel that we no longer need to see these messages now that we're up and
running but, unfortunately, you're probably wrong! Remember every program is
just waiting its opportunity to catch us out - so don't take any chances!
There are various ways we might overcome the problem of the disappearing
messages, but in this case we're going to create a replacement for the PRINT
statement that displays a string at any point on the screen for a given number of
seconds. The new function, DisplayMessage() is coded below:
FUNCTION DisplayMessage(x,y,message$,seconds#)
t = TIMER()
INK 0,0
millisecs = seconds# * 1000
WHILE TIMER() - t < millisecs
SET CURSOR x,y
PRINT message$
ENDWHILE
ENDFUNCTION
Activity 38.3
Add DisplayMessage() to the end of your program and replace all the PRINT
statements in the test stubs with calls to this function.
Adding SetUpGame()
This routine is a bit more complex since we need to set up the board, add the marbles,
position the selector and set up the Help feature. Since each of these will take up
several lines of code, it is best if we create new routines to handle each stage. This
means that the SetUpGame() function itself mainly consists of calls to other
functions, but it also initialises the gamestate variable:
FUNCTION SetUpGame()
CreateBoard()
CreateMarbles()
CreateSelector()
SetUpHelp()
gamestate = 1
ENDFUNCTION
Add the code for SetUpGame() to your program and add test stubs for the
routines that it calls.
Adding CreateBoard()
This routine is responsible for setting up the board. This means, not only does it
create a thin 3D box to represent the board, texturing it with the board image shown
in FIG-38.2, but it also fills the board and boardCoords arrays with relevant data.
While the visual aspects of creating the board require only a few lines, initialising
the arrays is much more complex, so again we'll make another routine to do that
part of the job (CreateInternalBoard()). The code for CreateBoard() is:
FUNCTION CreateBoard()
REM *** Set up board arrays ***
CreateInternalBoard()
REM *** Create and texture board ***
MAKE OBJECT BOX boardobj,27,27,0.5
LOAD IMAGE "board.bmp",boardimg
TEXTURE OBJECT boardobj,boardimg
REM *** Turn and position board ***
ROTATE OBJECT boardobj,90,0,0
POSITION OBJECT boardobj,-0.2,-1.2,0
ENDFUNCTION
Activity 38.5
Adding CreateInternalBoard()
Although this is a rather long routine, it doesn't do anything too complicated; just
fills the board and boardCoords arrays with the values shown earlier. The code for
the routine is:
FUNCTION CreateInternalBoard()
REM *** Initialise the board array ***
#CONSTANT EMPTYPIT 0
#CONSTANT NOTALLOWED -1
REM *** Assume every position empty ***
FOR row = 1 TO 7
FOR col = 1 TO 7
board(row,col) = EMPTYPIT
NEXT col
NEXT row
REM *** then mark the invalid positions ***
board(1,1) = NOTALLOWED
board(1,2) = NOTALLOWED
board(2,1) = NOTALLOWED
board(2,2) = NOTALLOWED
board(1,6) = NOTALLOWED
board(1,7) = NOTALLOWED
board(2,6) = NOTALLOWED
board(2,7) = NOTALLOWED
board(6,1) = NOTALLOWED
board(6,2) = NOTALLOWED
Activity 38.6
Although it won't make any visible difference to your program, execute the
code just to make sure there are no syntax errors or other problems.
Adding CreateMarbles()
Now we need to create the 3D marble objects. These will be placed just above the
pits on the board, except for the central pit which remains empty.
The logic for this routine can be described in structured English as:
Activity 38.7
Use the logic given above to produce the code for the CreateMarbles()
function and test your updated program.
Adding CreateSelector()
A blue outline cube is used to select which marble is to be moved and its destination.
The CreateSelector() function creates the cube placing it initially in the central pit.
The logic for the routine is:
Activity 38.8
Use the logic given to create the final version of the CreateSelector() routine
and add it to your program.
Check that the selector appears at the correct position on the board.
Adding SetUpHelp()
The player will be able to get help on the game rules and on which keys can be used
by pressing the F1 key (the traditional help key for Microsoft Windows programs).
The Help itself will be created as an animated sprite containing three frames; one
for each page of the Help (see FIG-38.6).
This routine will create the required sprite, but then hide the sprite. The sprite will
appear whenever the F1 key is pressed. If the user presses the F1 key a second time,
the Help sprite will disappear.
If you want to create your own Help image using a paint package, remember not to
use any black since it will automatically become transparent when the sprite is
displayed.
FUNCTION SetUpHelp()
REM *** Create the sprite required by help ***
CREATE ANIMATED SPRITE helpsprite,"rules.bmp",3,1,helpimg
REM *** Position sprite ***
Of course, we'll need another routine later to make the Help sprite appear when
required.
Activity 38.9
Add the SetUpHelp() function to your program. Check that the program still
compiles correctly.
Adding GetPlayerMove()
The GetPlayerMove() is the core routine of the game, so it will probably come as
no surprise that it is the most complex. However, it really only involves reading the
keyboard, recognising which key has been pressed and carrying out the required
action if it is valid. Invalid actions will produce error messages.
Unlike all the other functions, this one returns a value. If the user has chosen the
quit option, 1 is returned and zero for all other options.
Activity 38.10
Add test stubs for any routines called by GetPlayerMove() that are yet to be
written.
Adding MoveSelector()
The MoveSelector() function takes a parameter. This is the arrow key code and is
used to decide in which direction the selector is to be moved.
FUNCTION MoveSelector(code)
REM *** Move selector in response to arrow key ***
row = selectorposition.row
col = selectorposition.col
IF code = 203 AND col>1
DEC col
ELSE IF code = 205 AND col < 7
INC col
ELSE IF code = 200 AND row > 1
DEC row
ELSE IF code = 208 AND row < 7
Activity 38.11
Add the new routine to your program and check that the selector moves
correctly.
Adding SelectMarble()
In the first phase of a move (when gamestate = 1), the player has to select a marble.
This involves the player moving the selector over the required pit position and then
pressing Enter. At this point, control jumps to the SelectMarble() routine which
checks that the selected pit does, in fact, contain a marble (if not, an error message
is displayed) and changes the colour of the marble to indicate its selection. The
routine returns 1 when a marble has been selected and zero when no marble was
selected.
FUNCTION SelectMarble()
REM *** IF selector not over marble THEN ***
IF board(selectorposition.row,selectorposition.col) < 1
REM *** Display message; return 0 ***
DisplayMessage(100,100,"Choose a marble",2)
EXITFUNCTION 0
ENDIF
REM *** Change marble colour ***
COLOR OBJECT
board(selectorposition.row,selectorposition.col),
RGB(255,255,0)
REM *** record position of marble ***
move(1).row = selectorposition.row
move(1).col = selectorposition.col
ENDFUNCTION 1
Activity 38.12
As well as the obvious check that the selected pit must be empty, we also need to
check that it is exactly two pits from the selected marble in either a horizontal or
vertical direction. But even that's not all the checking required: moving the marble
to the selected pit must involve "jumping over" one other marble (the one that is to
be removed).
Checking for a valid move in the final IF mentioned above will be quite complex,
so we'll use another function, isValidMove(), to carry out the task. This function
will return 1 when the move is valid and zero when it's invalid.
Activity 38.13
Using the logic given, add the SelectPit() function to your program.
Adding IsValidMove()
There are three checks needed in this function:
Activity 38.14
Write the complete version of the IsValidMove() function and test it within
your program.
Adding MoveMarble()
Once a valid move has been entered, the selected marble must be moved to its new
position. On the screen, this not only involves moving the selected 3D sphere and
changing it back to its original texture, but also involves removing the "jumped"
Activity 38.15
Adding SelectHelpPage()
The final routine to be created is the SelectHelpPage() function. This function takes
a parameter (pageno) specifying the page number to be displayed. Since the Help
pages are stored as frames in an animated sprite, the pageno parameter represents
the frame to be displayed. If pageno is outside the range 0 to 3, then the function
exits; if the pageno is zero, the Help sprite is removed from the screen. The function
requires the following logic:
Activity 38.16
Code the SelectHelpPage() from the logic given and test your completed
program.
One way to overcome this problem is to place an invisible object at every valid pit
position, since the PICK OBJECT statement can be used to select invisible objects.
This means that pits which show a marble will actually contain two objects: a marble
and an invisible object; pits without a marble will contain only an invisible object.
To make a move the player will first click on a pit containing a marble and the mouse
position will be compared with the range of marble IDs to discover which one has
Although this will give us the ID values of the marble and invisible objects, we still
have to translate this into row and column values so that the board array can be
updated and the move array can be filled. We can find the selected marble's row and
column position by searching the board array for a match with the ID returned by
the PICK OBJECT statement (see FIG-38.7).
FIG-38.7
-1 -1 11 12 13 -1 -1
Finding the ID of the -1 -1 14 15 16 -1 -1
Selected Marble
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31 32 33 34 35 36 37
-1 -1 38 39 40 -1 -1
-1 -1 41 42 43 -1 -1
PICK OBJECT will return the ID of The ID can be looked up in the
the selected object board array finding the row and column
If we set up an array containing the IDs of the invisible objects, we can use the same
technique to find the row/column position of the empty pit selected as the second
part of a move.
Activity 38.17
Create a new project (Solitaire2.dbpro) and copy the source code for the first
version of the game into the new source file.
Copy all of the media files used into the new folder.
Since we are using a copy of the original program, it won't matter if things go wrong
- we can always go back.
We need to start by removing all references to the selector object since it won't be
needed in the new version of the game.
Activity 38.18
The next step is to add our invisible objects to each pit. We can't hide the objects
In the text we continue to
since PICK OBJECT doesn't detect hidden objects, but we can texture the objects
refer to the invisible with a black image and then use SET OBJECT TRANSPARENCY to make the
objects as being hidden. objects disappear.
FUNCTION CreateHiddenPitObjects()
REM *** Set first hidden object ID ***
pitobjno = firsthiddenobj
REM *** Load image used to texture objects ***
LOAD IMAGE "black.bmp",hiddenimg
REM *** FOR each position on board ***
FOR row = 1 TO 7
FOR col = 1 TO 7
REM *** IF its a valid position ***
IF board(row,col) > -1
REM *** Record hidden object ID for that pit
PitObjects(row,col) = pitobjno
REM *** Make, texture and hide object ***
MAKE OBJECT SPHERE pitobjno,2
TEXTURE OBJECT pitobjno,hiddenimg
POSITION OBJECT pitobjno,boardCoords(row,col).x,
Ä
0,boardCoords(row,col).z
SET OBJECT TRANSPARENCY pitobjno,1
REM *** Increment object ID
INC pitobjno
ENDIF
NEXT col
NEXT row
ENDFUNCTION
Activity 38.19
Add the constants and global variables mentioned above to your program.
Now we have to make some changes to the GetPlayersMove() function. The original
version of this function reacts to key presses, but in the update we need to allow for
both mouse and key presses to be detected. The routine starts by waiting for a key
press using the lines
REPEAT
code = SCANCODE()
UNTIL code <> 0
but in the new version, we need to wait for a key press or a mouse click and this is
achieved by the code:
REPEAT
mouse = MOUSECLICK()
code = SCANCODE()
UNTIL code <> 0 OR mouse <>0
The original SELECT statement that follows was based on the scan code of the key
pressed. Since we now have no use for a selector, detecting the arrow keys is no
The next CASE statement within the SELECT structure is CASE 28 which was
used to detect the Enter key. But now the mouse button needs to activate this option.
To keep our changes to a minimum, we can set code to 28 when the mouse is clicked:
IF mouse <> 0
code = 28
ENDIF
At the end of the SELECT structure, we waited for the button pressed by the player
to be released using the code
but now we also need to wait for the mouse button to be released, so our code should
be changed to:
REM *** Wait till all keys and mouse buttons released ***
WHILE SCANCODE() <> 0 OR MOUSECLICK() <> 0
ENDWHILE
Activity 38.20
The SelectMarble() routine needs to be changed. This version needs to detect the
mouse pointer position and select the marble over which it has been placed. The
logic for the updated routine is:
FUNCTION SelectMarble()
REM *** Check if position chosen contains marble ***
x = MOUSEX()
y = MOUSEY()
objno = PICK OBJECT(x,y,firstmarbleobj,lastmarbleobj)
IF objno = 0
DisplayMessage(100,100,"Choose a marble",2)
EXITFUNCTION 0
ENDIF
The search for the marble ID within the board array is very inefficient coding, but
for such a small search area will have no real effect on the speed of the game - and
it has the advantage of being quite simple.
Activity 38.21
The last routine to be changed is the SelectPit() function, which again needs to use
the mouse's position to determine which empty pit has been selected. The routine's
new logic is:
Activity 38.22
Use the above logic to code the SelectPit() function and test your program.
Suggested Enhancements
What we've developed in this chapter is the core of a game, but it still needs all those
nice finishing touches that gives a game that professional look.
Some of these would be simple to implement: use a larger font for messages; change
the textures used; an introductory splash screen; a finish screen; displaying the
number of marbles remaining; displaying the time elapsed; allowing the camera to
be moved.
Others would be a bit more complicated: keeping a top 5 best scores (least marbles
remaining); allowing the player to backtrack - undoing an unlimited number of
If you have time, see what enhancements you can come up with - you'll learn a lot
from the exercise.
REM *** Object constants *** REM *** Object constants ***
#CONSTANT boardobj 1 #CONSTANT boardobj 1
#CONSTANT selectorobj 2 #CONSTANT selectorobj 2
#CONSTANT firstmarbleobj 3 #CONSTANT firstmarbleobj 3
#CONSTANT lastmarbleobj 34 #CONSTANT lastmarbleobj 34
REM *** Image constants *** REM *** Image constants ***
#CONSTANT boardimg 1 #CONSTANT boardimg 1
#CONSTANT marbleimg 2 #CONSTANT marbleimg 2
#CONSTANT selectorimg 3 #CONSTANT selectorimg 3
#CONSTANT helpimg 4 #CONSTANT helpimg 4
REM *** Sprite constants *** REM *** Sprite constants ***
#CONSTANT helpsprite 1 #CONSTANT helpsprite 1
REM *** Declare data structure *** REM *** Declare data structure ***
TYPE Coords TYPE Coords
x AS INTEGER x AS INTEGER
z AS INTEGER z AS INTEGER
ENDTYPE ENDTYPE
REM *** Declare main variables *** REM *** Declare main variables ***
GLOBAL DIM board(7,7)`Marble positions GLOBAL DIM board(7,7) `Marble positions
GLOBAL DIM boardCoords(7,7) AS Coords GLOBAL DIM boardCoords(7,7) AS Coords
`Marble coordinates `Marble coordinates
GLOBAL selectorposition AS BoardPosition GLOBAL selectorposition AS BoardPosition
`Position of selector `Position of selector
GLOBAL DIM move(2) AS BoardPosition GLOBAL DIM move(2) AS BoardPosition
`marble and move-to position `marble and move-to position
GLOBAL gamestate GLOBAL gamestate
`Game state `Game state
`1-choose marble; `1-choose marble;
`2-choose space; `2-choose space;
`3-showing help `3-showing help
GLOBAL marblesRemaining GLOBAL marblesRemaining
`marbles on board `marbles on board
REM *** Main section *** REM *** Main section ***
SetUpScreen() SetUpScreen()
SetUpGame() SetUpGame()
REPEAT REPEAT
quit = GetPlayersMove() quit = GetPlayersMove()
UNTIL quit OR marblesremaining = 1 UNTIL quit OR marblesremaining = 1
WAIT KEY `***** Remove later **** WAIT KEY `***** Remove later ****
END END
Ä0,boardCoords(moves(2).row,moves(2).col).z
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(255,255,150)
TEXTURE OBJECT
Ä
BACKDROP ON
board(moves(2).row,moves(2).col),marbleimg REM *** Position camera ***
REM *** Calc position of jumped marble *** AUTOCAM OFF
jumpedrow = (moves(1).row+moves(2).row)/2 POSITION CAMERA 10,10,-20
jumpedcol = (moves(1).col+moves(2).col)/2 POINT CAMERA 0,0,0
REM *** Remove jumped marble from screen *** ENDFUNCTION
jumpedmarble = board(jumpedrow,jumpedcol)
HIDE OBJECT jumpedmarble FUNCTION SetUpGame()
DELETE OBJECT jumpedmarble CreateBoard()
REM *** Remove marble from board array *** CreateMarbles()
board(jumpedrow,jumpedcol)=0 CreateSelector()
REM *** Decrement marblesremaining *** SetUpHelp()
DEC marblesremaining gamestate = 1
ENDFUNCTION ENDFUNCTION
Ä
0,BoardCoords(row,col).z
board(jumpedrow,jumpedcol)=0
REM *** Decrement marblesremaining ***
ENDIF DEC marblesremaining
ENDFUNCTION ENDFUNCTION
The constants and globals should now be coded as: TYPE BoardPosition
row AS INTEGER
REM *** Constants *** col AS INTEGER
ENDTYPE
REM *** Object Constants ***
#CONSTANT boardobj 1 REM *** Declare main variables ***
#CONSTANT firstmarbleobj 3 GLOBAL DIM board(7,7)
#CONSTANT lastmarbleobj 34 `Marble positions
#CONSTANT firsthiddenobj 35 GLOBAL DIM boardCoords(7,7) AS Coords
#CONSTANT lasthiddenobj 67 `Marble coordinates
GLOBAL selectorposition AS BoardPosition
REM *** Image Constants *** `Position of selector
#CONSTANT boardimg 1 GLOBAL DIM move(2) AS BoardPosition
#CONSTANT marbleimg 2 `marble and move-to positions
#CONSTANT hiddenimg 3 GLOBAL gamestate
#CONSTANT helpimage 4 GLOBAL gamestate `Game state
`1-choose marble;
REM *** Sprite Constants *** `2-choose space;
#CONSTANT helpsprite 1 `3-showing help
FUNCTION MoveMarble()
REM *** Move marble in board ***
board(move(2).row,move(2).col) =
Ä
board(move(1).row,move(1).col)
board(move(1).row,move(1).col) = 0
REM *** Move marble on screen ***
POSITION OBJECT
Ä
board(move(2).row,move(2).col),
Ä
boardCoords(move(2).row,move(2).col).x,0,
Ä
boardCoords(move(2).row,move(2).col).z
TEXTURE OBJECT board(move(2).row,move(2).col),
Ä
marbleimg
REM *** Calculate position of jumped marble ***
jumpedrow = (move(1).row+move(2).row)/2
jumpedcol = (move(1).col+move(2).col)/2
REM *** Remove jumped marble from screen ***
jumpedmarble = board(jumpedrow,jumpedcol)
HIDE OBJECT jumpedmarble
DELETE OBJECT jumpedmarble
REM *** Remove jumped marble from board ***
board(jumpedrow,jumpedcol)=0
REM *** Decrement marblesremaining ***
DEC marblesremaining
ENDFUNCTION
FUNCTION SelectHelpPage(pageno)
IF pageno < 0 OR pageno > 3
EXITFUNCTION
ENDIF
IF pageno > 0
SET SPRITE FRAME helpsprite,pageno
SHOW SPRITE helpsprite
ELSE
HIDE SPRITE helpsprite
ENDIF
ENDFUNCTION
FUNCTION DisplayMessage(x,y,message$,seconds#)
t = TIMER()
INK 0,0
millisecs = seconds# * 1000
WHILE TIMER()-t < millisecs
SET CURSOR x,y
PRINT message$
ENDWHILE
ENDFUNCTION
Surface Reflection
In the real world, different materials reflect light in different ways. For example, a
red woollen coat, a red shiny metal sphere, and a red plastic box each reflect light
differently and hence give us a visual clue as to the material from which the object
is constructed. By modifying how a 3D object reflects light we can change its look
and so give the impression of different materials.
FIG-39.1 shows the conceptual ideas behind diffuse and specular reflection.
FIG-39.1
Parallel Parallel
rays ... ...are reflected in rays ... ...are reflected at the same
Diffuse and Specular all directions... angle as they arrive...
Reflection ? ?
Diffuse reflection creates surfaces that have a matt finish; specular reflection creates
a glossy surface.
FIG-39.2 shows the visual effect of both diffuse and specular reflection.
FIG-39.2
Diffuse and Specular
Reflection
Diffuse Specular
For example, we could switch off object 1's reflection of ambient light using the
statement:
The program in LISTING-39.1 creates a sphere, switches off the directional light,
and, after a key press, switches off the sphere's reflection of ambient light.
Modify the program so that the sphere's ambient light reflection is reactivated
after another key press.
If the light falling on an colour is an integer value specifying the diffuse colour
object is not white, the to be reflected. Where the colour of an object
diffuse colour should be
a combination of the has already been specified, it makes sense to use
object and light colours. that same colour value here (this assumes the
object is being lit by a white light).
For example, we could create a red diffuse reflection from object 1 using the line:
Activity 39.2
Create a program showing a blue sphere. Switch off the ambient reflection of
the sphere and set its diffuse reflection using cyan.
In the diagram:
Activity 39.3
Add a yellow spot light - position (-1,3,-5) - to your previous program and
create a key-activated yellow specular area on the sphere.
In the diagram:
The result of using value settings of 100 and 10 are shown in FIG-39.7.
FIG-39.7
Varying the Specular
Power Setting
The following code can be used to show the full range of specular reflection:
FOR power = 100 TO 0 STEP -1
SET OBJECT SPECULAR POWER 1,power
WAIT 25
NEXT power
Activity 39.4
Add the code given above to your last program and observe the effect.
In the diagram:
For example, we could create a blue emissive light from object 1 using the line:
Diffuse Emissive
It should be noted that the SET OBJECT EMISSIVE statement does not create true
light since it will not illuminate other, nearby objects.
Activity 39.6
Of course, all we have to do to make the sphere appear to emit light is to place a
point light at the centre of the sphere. This light should be the same colour as the
sphere's emissive light.
Activity 39.7
In your last program, add the following lines immediately before the final
WAIT KEY statement:
WAIT KEY
MAKE LIGHT 1
SET POINT LIGHT 1,0,0,0
COLOR LIGHT 1, RGB(0,0,255)
Test the program, observing how the new light affects the cube.
When another light shines on an object with emissive properties, the surface of that
object is shown in a colour created by a combination of the emissive colour and the
external light's colour. So a red directed light shining on a blue emissive sphere will
create magenta highlights on the sphere. This is demonstrated in LISTING-39.3.
In the diagram:
For example, maximum reflection from object 1 would be achieved using the line:
FIG-39.11
The Effects of using SET
OBJECT LIGHT
Activity 39.9
Use the SET OBJECT LIGHT statement to switch off normal light reflection
on the cube.
Change the colour of the ambient light to red. Does this affect the colour
reflected by the cube?
Bear in mind that the final reflection created by an object is determined by many
factors including the light sources, the object's reflective characteristics, and the
texture of the object.
FIG-39.12
Using Light Mapping
The function, CreateScene(), which creates the first version of the scene as shown
in FIG-39.12 is given below:
FUNCTION CreateScene()
REM *** Create cobbled street ***
MAKE OBJECT PLAIN 1,30,30
XROTATE OBJECT 1,90
LOAD IMAGE "cobbleslarge.jpg",1
TEXTURE OBJECT 1,1
POSITION OBJECT 1,0,-10,0
REM *** Create wall ***
MAKE OBJECT PLAIN 2,30,20
LOAD IMAGE "brickslarge.jpg",2
TEXTURE OBJECT 2,2
POSITION OBJECT 2,0,-1,5
REM *** Create lamppost ***
MAKE OBJECT CYLINDER 3,10
SCALE OBJECT 3, 5,100,5
POSITION OBJECT 3,0,-5,0
LOAD IMAGE "lamppost.jpg",3
TEXTURE OBJECT 3,3
MAKE OBJECT SPHERE 4,2,100,100
REM *** Make sphere appear to glow ***
SET OBJECT EMISSIVE 4,RGB(255,255,255)
REM *** Put light in sphere ***
MAKE LIGHT 1
SET POINT LIGHT 1,0,0,0
ENDFUNCTION
Activity 39.10
The light map images used to create the second version of the scene are shown in
FIG-39.13.
FIG-39.13
Examples of Light Maps
Light map for wall and street Light map for lamppost
(lightmap.jpg) (lampmap.jpg)
In the diagram:
The program in LISTING-39.4 uses the scene produced by CreateScene() and the
light maps shown in FIG-39.13 to create the effect shown in the second image of
FIG-39.12.
LISTING-39.4 REM *** Set up screen ***
SET DISPLAY MODE 1280,1024,32
Using a Light Map COLOR BACKDROP RGB(20,20,20)
BACKDROP ON
Activity 39.11
Modify your program to use this file as the light map for the wall and ground.
Unfortunately, if a 3D object is textured using tiling, the light map will also be tiled.
It's unlikely that we'll want this effect, so we must make sure that any object to
which we are going to apply a light map is textured by a single, untiled, image.
FIG-39.16
Bump Maps are Often
Based on the Texture
Image
For example, we could use the file named cobblesbump.jpg as a bump map for the
cobbled street in our previous program with the lines:
Activity 39.12
Add the lines given above, preceded by a WAIT KEY statement, to your last
program.
In the diagram:
Since a sphere map is meant to represent the reflection of objects on the surface of
a sphere, it only makes sense to apply this mapping to a spherical object. At first
glance, we may be tempted to think that the overall effect of this statement is really
Activity 39.13
Load the image stripes.jpg and use it to texture the sphere. The sphere should
now have a texture and sphere map image.
How does this affect the overall result when the sphere is rotated?
However, although the image displayed by sphere mapping does not change when
the object rotates, if the object begins to change position, then to maintain the
charade of being a reflection, the sphere mapped image needs to change its
perspective. This effect is demonstrated by the program in LISTING-39.6.
LISTING-39.6 REM *** Set up screen ***
SET DISPLAY MODE 1280,1024,32
The Reflection Changes
AUTOCAM OFF
as the Object Moves
POSITION CAMERA 0,10,-120
POINT CAMERA -30,0,0
REM *** Make sphere ***
MAKE OBJECT SPHERE 1,30,50,50
FIG-39.20
One Result Obtained by
Blending Two Images
We can achieve this effect in DarkBASIC Pro by using the SET BLEND MAPPING
ON statement which takes the format shown in FIG-39.21.
FIG-39.21
The SET BLEND SET BLEND MAPPING ON objno , imgno , mode
MAPPING ON
Statement
In the diagram:
The program in LISTING-39.7 blends the two images shown above, switching
mode (FROM 1 TO 11) every 5 seconds.
Activity 39.15
-z
-y
The format for the SET CUBE MAPPING ON statement is given in FIG-39.23.
FIG-39.23
The SET CUBE
MAPPING ON
SET CUBE MAPPING ON objno , imgno1 ,
Statement
NOTE: The images used must have dimensions which are some power of 2 (for
example, 64, 128, 256, etc.) otherwise the statement will not work.
Often the images used will be sky. If a sky sphere is being used, the images used
here can be copied from areas of that sky sphere's own image. In FIG-39.24 we see
how Adobe ImageReady has been used to slice a sky image into five 512 by 512
pixel images.
FIG-39.24
The Original Sky Image
The sixth image, which represents the base of our 3D space can be the cobbles we
used earlier, but modified to a 512 by 512 size. All the images to be used are
identified in FIG-39.25.
imgno3
FIG-39.25
The Sections Used in the
Cube Mapping
imgno5
imgno2 imgno1 imgno6
imgno4
Activity 39.16
Replace the rotating cube with a cone and see how this affects the result.
Activity 39.17
The transparency colour
defaults to black but can Type in and test the program in LISTING-39.9 (advanced09.dbpro).
be changed using the SET
IMAGE COLORKEY Notice that any part of the textured object which is the transparency colour is
statement. invisible irrespective of the opacity setting.
Shadows
The SET SHADOW SHADING ON Statement
A 3D object can be made to cast a shadow using the SET SHADOW SHADING
ON statement. This statement has the format shown in FIG-39.27.
When the last three parameters are omitted, the graphics card hardware is used to
calculate shadows. Not all video cards will be able to perform this operation.
Creating shadows requires a great deal of processing power, and the frame rate will
drop significantly when this option is used.
FIG-39.28
The Shadow Effect
Activity 39.18
Now give the distance parameter a value of 10. How does this affect the
shadow?
Normally, the shadow cast will be the shape of the object, but it is possible to cast
a shadow of a different shape by creating a mesh and using that mesh's ID as the
meshno parameter in the SET SHADOW SHADING ON statement (see
FIG-39.29).
FIG-39.29
The Cube Casts a Round
Shadow!
Activity 39.20
In your last program, switch off the cube's shadow effect when a key is
pressed.
OFF
Use SET GLOBAL SHADOW OFF to switch off shadows; use SET GLOBAL
SHADOWS ON to re-instate all shadows.
Activity 39.21
In the diagram:
For example, we could create a semi-transparent yellow shadow using the line:
Activity 39.22
Modify your last program so that the objects cast a semi-transparent red
shadow.
For example, if we knew three shadows might overlap and we wanted these overlaps
to display different degrees of darkness, we would set value to 3.
In your last program, add a line before the DO..LOOP structure to allow 2
shadow shadings.
Positioning Shadows
Of course, a shadow's position is dependent on the position of the light creating that
shadow; as we move the light, so the shadow will move.
Activity 39.24
In your last program, within the DO..LOOP structure, add the line:
Run the program and observe how the shadow moves as the light changes
position.
The SET SHADOW POSITION statement has the format shown in FIG-39.35.
FIG-39.35
The SET SHADOW SET SHADOW POSITION mode , x , y , z
POSITION Statement
In the diagram:
Activity 39.25
xpos = -100
Replace the POSITION LIGHT statement added in the last Activity by code to
increment xpos and set the shadow position to the coordinates (xpos,60,0).
How does this affect the shadows?
LISTING-39.12 shows the shadow effect produced by the Hivebrain model when
using the normal default settings for SET SHADOW SHADING ON.
LISTING-39.12 REM *** Set up screen ***
SET DISPLAY MODE 1280,1024,32
Model Shadows AUTOCAM OFF
POSITION CAMERA 0,10,-60
POINT CAMERA 0,0,0
Activity 39.26
As the program demonstrates, the shadow produced is far from realistic (see
FIG-39.36) .
To create a more convincing shadow, we need to turn the model into a mesh and
then specify that mesh in the SET SHADOW SHADING ON statement. This
requires the lines:
Activity 39.27
Modify your last program as described and observe how the shadow changes.
As we can see in FIG-39.37, the shadow is now an exact match for the model.
FIG-39.37
An Improved Shadow
Our next problem arises when we play an animated model - the shadow stays fixed
while the model itself changes. However, the shadow does at least move with the
model.
Activity 39.28
Add the following code just before the REM *** End program *** statement:
In the diagram:
Rather than texture a model in the normal way, cartoon shading colours the object
with the main colour of the shade image and slightly modifies the overall effect
using the edge image. Complex images have little impact on the final result, so the
images used with this statement are usually small, simply coloured ones.
Using a black edge image will make the object completely black; using a white edge
image allows the shade image to dominate in the final effect.
For example, we could use the images shade.bmp and edge.bmp to create a cartoon
shading on the Hivebrain model using the following lines:
Write a program to add cartoon shading to a plane and the Hivebrain model
used in the last program.
shade1.bmp, edge1.bmp,
shade2.bmp, edge2.bmp,
shade3.bmp, edge3.bmp
FIG-39.39 shows several examples of the images used and the overall result when
using cartoon shading on a plane and the Hivebrain model.
The image used needs to have an alpha channel, otherwise the overall effect is too
bright. FIG-39.41 shows the results achieved using an image with and without an
alpha channel.
Image Alpha Channel Result
FIG-39.41
Rainbow Shading and
Alpha Channels
No alpha channel
Modify your last program to use rainbow shading instead of cartoon shading.
Test the program with the files test1.tga, test2.tga and test3.tga which
correspond to the images used in FIG-39.41.
FUNCTION CreateSet()
REM *** Load images used ***
LOAD IMAGE "cobblestones.jpg",1
REM *** Create ground ***
MAKE OBJECT PLAIN 1,200,200
TEXTURE OBJECT 1,1
SCALE OBJECT TEXTURE 1,20,20
XROTATE OBJECT 1,-90
REM *** Load, scale and position alien ***
LOAD OBJECT "H-Alien Hivebrain-Move.x",2
SCALE OBJECT 2,300,300,300
POSITION OBJECT 2,0,0,0
ENDFUNCTION
Activity 39.32
In the diagram:
Activity 39.33
Replace the reflective cube in your last program with a 20 by 20 plane and
allow a key press to toggle the reflective ability of the plane.
Summary
l Different types of surfaces reflect light in different ways.
l Use SET OBJECT EMISSIVE to give the illusion that an object is emitting light.
l Use SET SHADOW SHADING OFF to turn off the shadow effect for a specified
object.
l Use SET GLOBAL SHADOW COLOR to set the colour-cast of all shadows.
l Use SET GLOBAL SHADOW SHADES to set the number of shades available
when shadows overlap.
l To create a realistic shadow from a model, use the mesh of the model as the
shadow.
l Use SET SHADING OFF to switch off all shading effects that have been applied
to a specific object.
Activity 39.14 The effect of the change on the author's machine was to
slightly decrease the frame rate but stop a slight flicker
No solution required. effect on the side of the cube.
DO
Activity 39.23
IF INKEY$() <> "" Replace the lines:
SET SHADOW SHADING OFF 2
ENDIF
REM *** Change shadow colour ***
TURN OBJECT LEFT 2,1
SET GLOBAL SHADOW COLOR 255,0,0,100
SET CURSOR 100,100
PRINT "Frame rate : ",SCREEN FPS()," ",
Ä
ord with:
LOOP
REM *** Set shadow shading ***
SET GLOBAL SHADOW SHADES 2
Activity 39.21
The frame rate will probably fall. The more shades
The frame rate increases significantly when shadows are specified, the lower the frame rate.
turned off.
Activity 39.24
Activity 39.22
The DO..LOOP code should now read:
REM *** Set up screen ***
SET DISPLAY MODE 1280,1024,32 DO
AUTOCAM OFF POSITION LIGHT 0,LIGHT POSITION X(0)
POSITION CAMERA 0,10,-60
POINT CAMERA 0,0,0
Ä+1,100,0
REM *** Shadow on/off options ***
REM *** Create background object ***
IF INKEY$() = "s"
MAKE OBJECT PLAIN 1, 100,100
SET GLOBAL SHADOWS ON
LOAD IMAGE "cobblestones.jpg",1
ENDIF
TEXTURE OBJECT 1,1
IF INKEY$() = "o"
SCALE OBJECT TEXTURE 1,10,10
SET GLOBAL SHADOWS OFF
XROTATE OBJECT 1, -90
ENDIF
REM *** Create and texture cube ***
REM *** Rotate cube and move sphere ***
MAKE OBJECT CUBE 2,20
TURN OBJECT LEFT 2,1
LOAD IMAGE "grid8by8.bmp",2
MOVE OBJECT 2, 0.1
TEXTURE OBJECT 2,2
MOVE OBJECT 3, movement#
POSITION OBJECT 2,0,20,0
IF OBJECT POSITION Z(3) < -40
REM *** Create and texture sphere ***
FUNCTION ScreenSetUp()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(255,255,100)
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,10,-30
POINT CAMERA 0,0,0
ENDFUNCTION
FUNCTION CreateSet()
REM *** Load images used ***
LOAD IMAGE "cobblestones.jpg",1
LOAD IMAGE "test3.tga",2
REM *** Create ground ***
MAKE OBJECT PLAIN 1, 200,200
TEXTURE OBJECT 1,1
SCALE OBJECT TEXTURE 1,20,20
XROTATE OBJECT 1,-90
REM *** Load, scale and place alien ***
LOAD OBJECT "H-Alien Hivebrain-Move.x"
Ä
,2
Unfortunately, things aren't as simple in 3D as they are in 2D, and this is reflected
by the number and complexity of the commands available for 3D collision handling.
There are two main classes of collision in 3D. The first, object collision, deals with
Moveable objects are collisions between moveable 3D objects (such as spheres, cones, cubes, and loaded
often referred to as
dynamic objects. models). The second type is static collision where a moving object collides with a
fixed (static) object (for example, a character colliding with the side of a house).
Object Collision
When one 3D object collides with another, we need to detect this and make the
objects react to the event in a realistic way.
Activity 40.1
What happens when the sphere comes into contact with the cube?
As we saw from the Activity above, objects will not automatically react to making
contact with each other. If we want some reaction, then we must include explicit
code to deal with this in our program.
integer
In the diagram:
When using the first option, where both objects are specified using ID numbers, the
statement returns 1 if the objects have collided and zero if no collision has taken
place. For example, in the last program we could check for the sphere and cube
colliding using the statement:
IF OBJECT HIT(1,2) = 1
When we are unsure of which object may have been hit, we can use the second
version of this statement (with zero as the final value). This will return the ID
number of any object struck by objno1. If no object has been hit, then zero is
returned. For example, we could discover the ID of any object struck by object 1
with the code:
Activity 40.2
Add a second DO..LOOP structure near the end of the program to allow the
player to move the camera using the arrowkeys.
Run the modified version of the program and manually position the camera so
that you can see that the sphere has come into contact with the cube.
FIG-40.2
objno2
The OBJECT
COLLISION Statement OBJECT COLLISION ( objno1 , )
0
integer
In the diagram:
Activity 40.3
Modify your last program to use the OBJECT COLLISION statement in place
of OBJECT HIT.
FIG-40.3
ON
The SET OBJECT
COLLISION Statement SET OBJECT COLLISION objno
OFF
In the diagram:
When an object has its collision detection switched off using this statement, any
subsequent calls to OBJECT HIT or OBJECT COLLISION which involve this
object will always return zero.
Switch off collision detection for object 1 in your last program and check out
how this affects the results obtained.
The bounding sphere is In addition to the collision box, every object is also surrounded by a bounding or
really a polyhedron. collision sphere since, for some 3D objects, this creates a more accurate
approximation of the shape it surrounds than a box. For example, FIG-40.5 shows
the box and sphere that surround each of the objects in our previous program.
FIG-40.5
Collision Boxes
In the diagram:
Activity 40.6
Modify your last program so that the bounding boxes of both objects are
displayed.
Activity 40.7
Change the cube in your program to a cone. How does this change affect the
collision between the two objects?
This last Activity highlights a problem: boxes and spheres do not make ideal
collision volumes for every shape.
In the diagram:
Activity 40.8
In the last program, use HIDE OBJECT BOUNDS to hide the bounding boxes
of the sphere and cone after a key is pressed and before movement starts.
FIG-40.8
A Collision Box Rotates
Along with the Object to
which it is Attached
By default, the collision boxes are always used to detect collision between two 3D
objects - even between two spheres. The program in LISTING-40.2 fires the sphere
in the direction of the rotated cuboid shown in FIG-40.8 above. Since the objects'
collision boxes do not intersect, no collision is detected.
LISTING-40.2 REM *** Setup screen ***
SET DISPLAY MODE 1280,1024,32
Only a Hit Between Two COLOR BACKDROP 0
Collision Boxes is BACKDROP ON
Detected
REM *** Position camera ***
AUTOCAM OFF
POSITION CAMERA 100,10,-200
POINT CAMERA 0,0,0
REM *** Move sphere towards box until they collide ***
DO
POSITION OBJECT 2,0,20,OBJECT POSITION Z(2)+1
IF OBJECT HIT(1,2)
EXIT
ENDIF
LOOP
REM *** End program ***
WAIT KEY
END
We can see from running this program that, although the bounding spheres of both
3D objects intersect, no collision is detected.
In the diagram:
Activity 40.10
In the diagram:
Move the sphere in your previous program to a height of 40 and run the code.
Does the 3D sphere hit the cuboid's bounding sphere?
Change the radius of the cuboid's bounding sphere to 50 and re-run the
program. Does the 3D sphere collide with the modified bounding sphere?
Unfortunately, the SHOW OBJECT BOUNDS statement only displays the default
bounding box and sphere, so it does not show any changes to the bounding sphere's
size when the radius is changed.
real
In the diagram:
real
In the diagram:
Activity 40.12
We can force the computer to detect collision with the actual polygons that make
up the 3D shape itself by using the SET OBJECT COLLISION TO POLYGONS
statement, the format for which is shown in FIG-40.14.
FIG-40.14
The SET OBJECT SET OBJECT COLLISION TO POLYGONS objno
COLLISION TO
POLYGONS Statement
In the diagram:
In LISTING-40.3 the program again fires the sphere towards the cone shape, but
this time using polygon collision detection.
REM *** Move sphere towards cone until they collide ***
DO
POSITION OBJECT 2,0,10,OBJECT POSITION Z(2)+1
IF OBJECT HIT(1,2)
EXIT
ENDIF
LOOP
Activity 40.13
Using polygon collision detection is very process-intensive and can slow down your
program, so don't use it unnecessarily.
x1 , y1 , z1 , x2 , y2 , z2 , rotateflag
In the diagram:
The diagram in FIG-40.16 shows the position of the vertices to be given when
creating a collision box.
FIG-40.16 x2,y2,z2
User-defined
The Coordinates Required collision box
When Specifying a Collision
Box
3D Object
x1,y1,z1
Normally, a collision box will rotate along with the 3D object to which it is attached.
For example, FIG-40.17 shows the position of a cone (object 1) and its
corresponding collision box after the statement
ZROTATE OBJECT 1, 45
3D Object
By using a setting of zero for rotateflag, the collision box will not rotate along with
the object to which it is attached. For example, if we execute the lines
rotating the cone by 45o, then the cone and its collision box will be as shown in
FIG-40.18.
FIG-40.18 User-defined
collision box
The Collision Box Does
Not Rotate
3D Object
cuboid
REM *** Move sphere towards box until they collide ***
DO
POSITION OBJECT 2,0,30,OBJECT POSITION Z(2)+1
SET CURSOR 100,100
PRINT OBJECT POSITION Z(2)," ",STATISTIC(1)
IF OBJECT COLLISION(1,2)
EXIT
ENDIF
LOOP
Modify the program so that the cuboid is rotated to 90o about its z-axis after
the collision box has been created.
The first time the cuboid is rotated, the sphere still collides with its bounding box
because the bounding box no longer rotates in conjunction with its object.
FIG-40.20
A Sphere Moving
Through a Box
FIG-40.21
The GET OBJECT X
COLLISION Statement
GET OBJECT COLLISION Y ( )
real
Normally, we would use all three options to get the full details of the collision:
By adding the penetration depth to the moving object's position, it will move back
outside the first object's collision box:
REM *** Move sphere towards box until they collide ***
DO
REM *** Move sphere 1 unit along z-axis ***
POSITION OBJECT 2,OBJECT POSITION X(2),
Ä OBJECT POSITION Y(2),OBJECT POSITION Z(2)+0.1
REM *** IF sphere collides with box ***
IF OBJECT COLLISION(1,2)
REM *** Calculate penetration values ...***
x#=GET OBJECT COLLISION X()
y#=GET OBJECT COLLISION Y()
z#=GET OBJECT COLLISION Z()
REM *** ... and use them to reset sphere's position ***
POSITION OBJECT 2,OBJECT POSITION X(2)+x#,
ÄOBJECT POSITION Y(2)+y#,OBJECT POSITION Z(2)+z#
ENDIF
LOOP
REM *** End program ***
END
What happens when the sphere comes into contact with the cuboid?
The effect becomes more interesting when the sphere is moving with an offset in
two or three axes. For example, we can make the sphere move in both the y and z
axes by changing the first line within the DO loop to read:
Activity 40.16
Make the change suggested above to your last program. How does this affect
the movement of the sphere?
This effect is known as a sliding collision, for obvious reasons, with the sphere
appearing to slide along the surface of the cuboid until it's free to move along its
old trajectory.
Activity 40.17
In your previous program, delete the cuboid's bounding box. Does the sphere
halt because of collision?
REM *** Move sphere towards box until they collide ***
DO
POSITION OBJECT 2,0,10,OBJECT POSITION Z(2)+1
SET CURSOR 100,100
PRINT OBJECT POSITION Z(2)
LOOP
Activity 40.18
FIG-40.25
The INTERSECT
INTERSECT OBJECT ( objno ,
OBJECT Statement
x1 , y1 , z1 , x2 , y2 , z2 )
real
In the diagram:
The statement returns the shortest distance between the target object and the line
specified. If a value of zero is returned, the line and object intersect.
For the wand and sphere example mentioned earlier, x1,y1,z1 would specify any
point on or near the front of the wand. While x2,y2,z2 would specify a point at the
end of the range of the lightning bolt. The line created by joining these two points
should be parallel to the body of the wand. objno would be the object number
assigned to the sphere. This example is shown in LISTING-40.7.
LISTING-40.7 REM *** Set up screen ***
SET DISPLAY MODE 1280,1024,32
Intersection between an COLOR BACKDROP 0
Line and an Object BACKDROP ON
DO
SET CURSOR 100,100
PRINT dist#
LOOP
Activity 40.19
Summary
l Use OBJECT HIT or OBJECT COLLISION to determine if two objects have
collided.
l Use SET GLOBAL COLLISION to switch collision detection for every object
on or off.
l Collision detection using boxes and spheres is fast but can be inaccurate.
l Collision with the actual polygons that make up the object itself can be used to
give accurate collision detection, but requires much more complex calculations.
l Use SET OBJECT RADIUS to modify the size of an object's bounding sphere.
l Use OBJECT COLLISION RADIUS to find out the current radius of an object's
bounding sphere.
l Use the data from GET OBJECT COLLISION to create a sliding collision effect.
In the diagram:
FIG-40.27
The GET STATIC GET STATIC COLLISION HIT
COLLISION HIT
Statement
x1 , y1 , z1 , x2 , y2 , z2
x3 , y3 , z3 , x4 , y4 , z4
Static collision
box
The GET STATIC COLLISION HIT statement returns 1 if the leading box (that is,
the box defined using x3,y3,z3,x4,y4,z4) intersects any part of the static collision
box. If there is no intersection, zero is returned.
Activity 40.20
FIG-40.29 X
The GET STATIC
COLLISION Statement GET STATIC COLLISION Y ( )
real
Normally, we would use all three options to get the full details of the collision:
The next program (LISTING-40.9) is a modification of the last one. This time the
asterisks are replaced by the values returned by GET STATIC COLLISION
statements. Also, the display uses the SYNC statement so that we can pause the
program with a WAIT KEY statement and still see the values output by the PRINT
statement.
REM *** Record x and y of old and new bounding boxes ***
x1# = OBJECT POSITION X(2)-2.5
x2# = x1#+5
x3# = x1#
x4# = x2#
y1# = OBJECT POSITION Y(2)-2.5
y2# = y1#+5
y3# = y1#
y4# = y2#
Activity 40.21
As we can see from the figures, as the leading bounding box enters the collision
area the value returned by GET STATIC COLLISION Z is determined by the result
of the calculation
which is simply:
z4 - 1
Activity 40.22
Change the first z value of the static collision box from -1 to -2 (thereby
making the static collision box 3 units thick) and run the program again.
Notice that the last value displayed by GET STATIC COLLISION Z is -0.5. Once
the mid-point of the trailing bounding box is passed the mid-point of the static
collision box, the formula for the value returned by GET STATIC COLLISION Z
changes to:
Activity 40.23
a) Changing the static collision box's z ordinates from -2 and 1 to -5 and 10.
b) Making the sphere enter from the other side of the static collision box
(that is, start the sphere at position (0,10,100) and give it a speed of -1).
So how can we make use of the value returned by the GET STATIC COLLISION
values in our program? Well, if we use the value returned by the statement to modify
the position of our moving object, we can stop that object moving through the static
collision box area.
Activity 40.24
DO
REM *** Move sphere ***
POSITION OBJECT 2,0,10,OBJECT POSITION Z(2)+1
REM *** Calculate z ordinate for old and new boxes ***
z1# = OBJECT POSITION Z(2)-5.5
z2# = z1# + 5
z3# = z1# + 3
z4# = z2# + 3
REM *** Check for collision with static collision box ***
IF GET STATIC COLLISION HIT(x1#,y1#,z1#,x2#,y2#,z2#,
Äx3#,y3#,z3#,x4#,y4#,z4#)
REM *** Get penetration details ***
x# = GET STATIC COLLISION X()
y# = GET STATIC COLLISION Y()
z# = GET STATIC COLLISION Z()
REM *** Move sphere back out of static collision box ***
POSITION OBJECT 2,0,10,OBJECT POSITION Z(2) - z#
ENDIF
REM *** Update screen ***
SYNC
LOOP
Run the program. What happens when the sphere hits the static box?
As you have seen, we can use the GET STATIC COLLISION value to force the
sphere to stop when it hits the static collision box.
When the moving object contains movement along a second axis, then the overall
effect is to slide the moving object along the surface of the static collision box.
REM *** Record x for old and new bounding boxes for sphere ***
x1# = OBJECT POSITION X(2)-2.5
x2# = x1#+5
x3# = x1#
x4# = x2#
Activity 40.25
As a general rule, since we may not know through which axes an object is moving,
the values x1# to z4# should be calculated on every iteration of the main loop and
the repositioning after a hit should include all three penetration figures:
, width , latitude )
integer
In the diagram:
x2,y2,z2 are a set of real values giving the end point of a line.
This statement returns 1 if the line starting at point (x1,y1,z1) and passing through
the point (x2,y2,z2) eventually hits an existing static collision box; otherwise zero
is returned. In performing the calculation the line is assumed to be in the form of a
cylinder with diameter width.
Assuming a weapon's centre is at point (-40,0,0) and that the weapon has been
rotated by 90o about the z-axis, with one point along the line of sight being (-5,0,0),
then we can check if this line of sight hits a static collision box using the line:
In this example, the line has been given a thickness of 1 and the latitude setting is
also set to 1 (thereby specifying a high degree of accuracy in the calculation).
Activity 40.26
real
In the diagram:
For example, we could record the point of contact between the line of sight and a
static collision box with the lines:
Modify your previous program to display the point of intersection between the
light of sight and static collision box.
The program in LISTING-40.12 creates a camera and a static collision box. The
camera is then moved to a new position using the SET CAMERA TO FOLLOW
statement and this requires the camera to come into contact with the collision box.
LISTING-40.12 REM *** Setup screen ***
SET DISPLAY MODE 1280, 1024, 32
The SET CAMERA TO COLOR BACKDROP RGB(255,255,100)
FOLLOW Statement and BACKDROP ON
Static Collision Boxes
REM *** Position camera ***
AUTOCAM OFF
POSITION CAMERA 0,0,-50
POINT CAMERA 0,0,0
Activity 40.28
Summary
l Static bounding (collision) boxes are used to create a detectable collision volume
around fixed areas.
l Data from GET STATIC COLLISION can be used to create a sliding collision
of an object against a static bounding box.
DO
POSITION OBJECT 2,0,0, Activity 40.6
Ä
OBJECT POSITION Z(2)+0.1
REM *** Setup screen ***
IF OBJECT COLLISION(1,2)
SET DISPLAY MODE 1280, 1024, 32
EXIT
COLOR BACKDROP 0
ENDIF
BACKDROP ON
LOOP
REM *** Position camera ***
AUTOCAM OFF
POSITION CAMERA 100,10,-200
Activity 40.4 POINT CAMERA 0,0,0
REM *** Setup screen *** REM *** Make two objects ***
SET DISPLAY MODE 1280, 1024, 32 MAKE OBJECT CUBE 1, 40
COLOR BACKDROP 0 MAKE OBJECT SPHERE 2, 5
BACKDROP ON POSITION OBJECT 2, 0,0,-100
REM *** Position camera *** REM *** Show bounding boxes ***
AUTOCAM OFF SHOW OBJECT BOUNDS 1,1
POSITION CAMERA 100,10,-200 SHOW OBJECT BOUNDS 2,1
POINT CAMERA 0,0,0 REM *** Move sphere towards cube ***
REM *** Make two objects *** DO
POSITION OBJECT 2,0,0,
MAKE OBJECT CUBE 1, 40
MAKE OBJECT SPHERE 2, 5 Ä OBJECT POSITION Z(2)+1
POSITION OBJECT 2, 0,0,-100 IF OBJECT COLLISION(1,2)
REM *** Switch off cube's col'sn detect *** EXIT
SET OBJECT COLLISION OFF 1 ENDIF
REM *** Move sphere towards cube *** LOOP
DO DO
CONTROL CAMERA USING ARROWKEYS 0, 0.1,
POSITION OBJECT 2,0,0,
Ä
OBJECT POSITION Z(2)+1 Ä 0.5
IF OBJECT COLLISION(1,2) LOOP
EXIT REM *** End program ***
ENDIF END
LOOP
DO
The objects should now be created using the lines: POSITION OBJECT 2,0,40,-100
Two lines need to be changed to lift the sphere to 40 units. Rotating the cuboid does not rotate the collision box and
Change therefore a collision still occurs.
POSITION OBJECT 2,0,20,-100 By changing the last value in the MAKE OBJECT
No solution required. The camera STILL seems to pass through the collision
box!
Activity 40.21
No solution required.
Activity 40.22
No solution required.
Activity 40.23
The formula still holds in both cases.
Activity 40.24
The sphere halts when the static collision box is
encountered.
Activity 40.25
No solution required.
Activity 40.26
No solution required.
Activity 40.27
REM *** Set up screen ***
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON
The particles shoot upwards and then fall back to an imaginary ground area before
eventually fading.
There are various characteristics that can be set, such as the quantity, speed, and
duration of the particles. It will be useful if we think of a particle object as having
two components: the emitter, and the particles themselves. The particles all
originate from the emitter, which is at the centre of the particle display.
Creating Particles
The MAKE PARTICLES Statement
To create a stream of particles we'll need an image. This image is used on each
particle created, but seems to have only a minor visual effect on what you'll see on
the screen.
With an image loaded, you can now use the MAKE PARTICLES statement to create
a continual stream of particles which originate from a single point (called the
emitter), fly upwards and then fall back down before finally disappearing. The
statement has the format shown in FIG-41.2.
FIG-41.2
The MAKE PARTICLES
MAKE PARTICLES partno , imgno , partflag , size
Statement
Activity 41.1
Change the value of size to 10 and see how this affects the particles.
Activity 41.2
In the diagram:
In the diagram:
In the diagram:
Activity 41.3
Modify the code so that the particles object is moved up the screen rather than
from left to right.
In the diagram:
The program in LISTING-41.3 only differs from the previous program by a single
line of code, but the effect on the screen is quite different.
Activity 41.4
Modify your previous program to match the code given above and check out
the difference between using POSITION PARTICLES and POSITION
PARTICLE EMISSIONS.
The emitter is now facing the camera There is no apparent change to The particles now shoot downwards
and the particles shoot towards the the display from its default setting. and return upwards to the surface.
viewer.
It's easier to get your head round this if you think of the whole particles effect as
creating a cone-shaped display, with the particles rising straight up through the
centre of the cone and then falling back round its base perimeter. Now we can show
the effects of each rotation in a more abstract way (see FIG-41.10).
In the diagram:
The particles are actually set to a colour determined by the colour in the original
image plus the colour specified here. By using a pure white image when setting up
the particles object, the COLOR PARTICLES statement causes the particles to
The program in LISTING-41.4 randomly changes the colour of the particles every
second for 10 seconds.
Activity 41.5
This statement has no effect on the height or distance over which the particles travel.
Activity 41.6
Modify your last program to match that given above and observe the effects
produced.
FIG-41.13
The SET PARTICLE
SET PARTICLE VELOCITY partno , value
VELOCITY Statement
In the diagram:
This statement affects the height and distance over which the particles travel.
Activity 41.7
FIG-41.14
The SET PARTICLE
SET PARTICLE GRAVITY partno , pull
GRAVITY Statement
In the diagram:
This statement affects the height and distance over which the particles travel.
Activity 41.8
Replace the SET PARTICLE VELOCITY statement with the equivalent SET
PARTICLE GRAVITY statement. Test the resulting program.
FIG-41.15
The SET PARTICLE
SET PARTICLE CHAOS partno , factor
CHAOS Statement
In the diagram:
Activity 41.9
In your last program, change the FOR loop to start at -50 and finish at 50.
Replace the SET PARTICLE GRAVITY statement with the equivalent SET
PARTICLE CHAOS statement.
Reduce the wait time for each iteration from 1000 to 200.
When handling particles, it is possible to specify how much time has passed between
each screen frame using the SET PARTICLES SPEED statement. This will affect
the apparent speed of the particles and the number of particles appearing, since
some will be created and destroyed in the time between two frames.
In the diagram:
The default setting for the gap between frames is 0.005 seconds.
Activity 41.10
FIG-41.17
The SET PARTICLE
SET PARTICLE FLOOR partno , floorflag
FLOOR Statement
In the diagram:
This statement effects the height and distance over which the particles travel.
Since the normal lifespan of a particle is 100%, we could halve this duration using
the statement
Activity 41.12
In your last program, add a particle life of 20%. This statement should be
added immediately after the particles object is created.
Activity 41.13
Place the GHOST PARTICLES ON statement within a FOR loop using the
loop counter (which should range from 0 to 5) as the last value in the GHOST
PARTICLES ON statement. Wait for a key press between each value.
integer
In the diagram:
If the specified particles object does exist, then 1 is returned; otherwise zero is
returned.
IF PARTICLES EXIST(1) = 0
MAKE PARTICLES 1,1,1,10
ENDIF
real
In the diagram:
For example, we could determine the position in space of object 1's centre using the
lines:
In the diagram:
In the diagram:
Summary
l Using a particles object creates a sparkler effect.
l The colour of the particles emitted is initially determined by the image associated
with the particles object.
l Use POSITION PARTICLES to move the particles object's emitter and currently
visible particles.
l Use ROTATE PARTICLES to rotate the particles object's emitter about its local
axes.
l Use SET PARTICLE VELOCITY to modify the speed at which particles move
away from the emitter.
l Use SET PARTICLE GRAVITY to affect how particles fall after being emitted.
l Use SET PARTICLE CHAOS to affect the random attributes of the particles'
paths.
l Use SET PARTICLE SPEED to modify the time elapsed between each screen
update of the particles' positions.
l Use SET PARTICLE LIFE to set the life time of the particles.
l Use GHOST PARTICLES OFF to return particles to a normal state after using
GHOST PARTICLES ON.
The Statements
The MAKE SNOW PARTICLES Statement
Particles can be made to fall like snow from the skies using the MAKE SNOW
PARTICLES statement. Rather than originate from a single emitter point, snow
particles are created over a rectangular area and fall for a set distance before
disappearing. The overall effect is to produce a box of falling particles. The MAKE
SNOW PARTICLES statement has the format shown in FIG-41.25.
FIG-41.25
The MAKE SNOW MAKE SNOW PARTICLES partno , imgno , freq ,
PARTICLES
Statement
x , y , z , w , h , d
In the diagram:
Activity 41.14
We might reasonably suppose that the position specified in the MAKE SNOW
PARTICLES statement ((0,0,0) in the example above) represents the exact centre
of the snow box, but, in fact, this is not the case.
Activity 41.15
Modify your last program, placing a 1 unit cube at position (0,0,0). Is the cube
at the centre of the snow area?
As you can see, the cube, which has been placed at the same position as that given
in the MAKE SNOW PARTICLES statement, is not in the centre of the snow box.
In fact, the box is centred in the x and z axes but about 75% of the snow falls below
the cube's centre and about 25% above. Remember to take this into account
whenever placing snow in your scene.
Some, but not all, of the normal particles statements can be applied to snow particles.
For example, snow can be moved to a different position, rotated, shown and hidden,
but none of the SET PARTICLE statements operate.
Activity 41.16
FIG-41.26
MAKE FIRE PARTICLES partno , imgno , freq ,
The MAKE FIRE
PARTICLES
Statement
x , y , z , w , h , d
Activity 41.17
The same restrictions apply to fire particles as snow particles with many of the
regular particles statements not operating on these particles objects.
Summary
l Use MAKE SNOW PARTICLES to create a snowing effect.
l The box in which the snow appears is based around the position (x, y, z) but,
although centred on the x and z values, is about 75% below the y value.
l Only some of the standard particles object statements can be applied to snow and
fire particles.
A Roman Candle
All we need is a cylinder and a particles object to create a roman candle firework
(see FIG-41.27) as coded in the program given in LISTING-41.9.
LISTING-41.9 REM *** Define constants ***
REM *** Image IDs ***
A Roman Candle #CONSTANT YellowImg 1
#CONSTANT WrapperImg 2
Activity 41.18
Modify the program so that the firework emits both red and yellow particles.
(HINT: Add a second particles object and use the image red.bmp.)
Activity 41.19
Modify the program so that the spaceship's engine blast only appears after a
key press. The rocket should then actually take off, moving out of the top of
the screen. Make the rocket also move in a z direction as it lifts - this will help
give the illusion of it moving away from the viewer.
A Dungeon Torch
It's a well-known fact that a proper dungeon always uses flaming torches to light
their long dank corridors. By combining particles and a light map, we can recreate
an illusion of that setup in DarkBASIC Pro.
The torch itself is just a cone and a particle emitter. A point light is placed at
approximately the same position as the particles. The light map used back in Chapter
34 is used to create a circle of light on the wall. Finally, to create a flickering effect
the light's range continually changes (see FIG-41.29 and LISTING-41.11).
LISTING-41.11 SetUpScreen()
CreateScene()
A Dungeon Torch
DO
SET LIGHT RANGE 1, RND(1000)
WAIT 10
LOOP continued on next page
FUNCTION CreateScene()
REM *** Create wall ***
MAKE OBJECT PLAIN 2,30,20
LOAD IMAGE "brickslarge.jpg",2
TEXTURE OBJECT 2,2
POSITION OBJECT 2,0,-1,5
REM *** Create torch holder ***
MAKE OBJECT CONE 3,3
SCALE OBJECT 3,25,100,25
ZROTATE OBJECT 3,180
XROTATE OBJECT 3, 30
POSITION OBJECT 3, 0,-2,4.5
FIG-41.29 REM *** Create flames ***
LOAD IMAGE "yellow.bmp",3
A Flaming Torch MAKE PARTICLES 1,3,1,1
POSITION PARTICLES 1, 0,-1,4
ROTATE PARTICLES 1,-30,0,0
SET PARTICLE VELOCITY 1, 5
SET PARTICLE GRAVITY 1,-1
SET PARTICLE LIFE 1,10
SET PARTICLE SPEED 1,0.002
REM *** Put light in sphere ***
MAKE LIGHT 1
SET POINT LIGHT 1,0,1,2
SET LIGHT RANGE 1,1000
REM *** Add Light maps ***
LOAD IMAGE "lightmap.jpg",4
SET LIGHT MAPPING ON 2,4
LOAD IMAGE "lampmap.jpg",5
SET LIGHT MAPPING ON 3,5
ENDFUNCTION
Activity 41.20
Activity 41.20
No solution required.
The Equipment
The game consists of a board showing numbered squares, the player's character and
a dice. The board squares are laid out in horizontal rows, with 10 squares per row.
The squares are numbered sequentially from 1 to 100. Also on the board are a set
of elevators used to carry the player from one level to another.
The Aim
The aim of the game is to get the player's character from square 1 to square 100.
The Rules
The basic rules come from the snakes and ladders game. The player starts on square
1 and moves forward according to the value thrown on the dice. If, at the end of a
move, the character is on an elevator platform, it takes that elevator to the destination
square - sometimes upwards; sometimes down.
Game Responses
The game reacts to a dice throw by moving the player's character by the value
thrown. If the character has to move from one row of the board to the next during
a move, this is done using the lift at the end of the row. If the character stops on an
elevator square it is carried off to that elevator's destination.
Screen Layout
To create an unique look to the game, it is played in 3D with each row of the board
becoming a corridor down which the player's character is to move. The camera view
gives a 3rd person perspective, the scene being shown from a viewpoint just behind
the character's shoulder (see FIG-42.1).
Notice that all the lifts are vertical. Anything lying at an angle would just cause
more modelling problems.
The other images required are the textures for the 3D objects and are shown in
FIG-42.3.
Data Structures
We need to set up a record structure to hold details about a single elevator. The
information required is:
TYPE ElevatorType
ID AS INTEGER
startsquare AS INTEGER
There are 27 elevators in total, so we need an array to store information about each:
The board itself need only record, for each square, the presence of an elevator
starting on that square:
GLOBAL DIM board(100) AS INTEGER
All we need to know about the player's character is its position on the board:
GLOBAL playerat AS INTEGER
The 3D objects used in the game have already been textured and only need to be
assigned ID values within the game. As a general rule we should assign a name to
each ID, but since there are 27 elevators we'll assign a name to the first one only.
So, the final code for our main data and constants is:
REM **********************************
REM *** Structures ***
REM **********************************
REM *** Elevator details ***
TYPE ElevatorType
ID AS INTEGER
startsquare AS INTEGER
endsquare AS INTEGER
floors AS INTEGER `(-ve for downwards)
zord AS INTEGER
ENDTYPE
REM **********************************
REM *** Global variables ***
REM **********************************
GLOBAL DIM Lifts(27) AS ElevatorType
GLOBAL DIM board(100) AS INTEGER
GLOBAL playerat AS INTEGER
REM **********************************
REM *** Constants ***
REM **********************************
REM *** Object constants ***
#CONSTANT boardobj 1
#CONSTANT playerobj 2
#CONSTANT diceobj 3
#CONSTANT firstliftobj 4 `27 lifts with IDs 4 - 30
Game Logic
As usual, we want to describe the whole logic of the program in less than a dozen
lines and then look at each of these sections in more detail until we have our final
product. So, the overall game could be described as follows:
REPEAT
As usual, the descriptive lines become function calls in the actual program. We'll
need to find out what value is thrown on the dice so it can be used when moving
the player, so the function for throwing the dice must return the dice value and the
function which moves the player must take that value as a parameter. This gives us
the following code for the main section:
REM **********************************
REM *** Main game logic ***
REM **********************************
SetUpGame()
REPEAT
thrown = RollDice()
MovePlayer(thrown)
UNTIL playerat = 100
EndScreen()
END
Activity 42.1
Create a new project (elevators.dbpro) and copy into it the code already given
for the data structure, global variables and constants as well as the logic of the
main section.
At the end of the code, add a REM statement containing the lines:
REM ************************************
REM *** Level 1 routines ***
REM ************************************
Under these REM statements, write test stubs for each function called. The test
stubs should be empty, containing only FUNCTION and ENDFUNCTION
statements.
Run the program and make sure it executes. You'll have to press Escape to exit
the REPEAT..UNTIL structure in the main section.
Adding SetUpGame()
There are many tasks to be performed by this part of the program, but we can split
those tasks into two main sections:
FUNCTION SetUpGame()
InitialiseData()
InitialiseVisuals()
ENDFUNCTION
REM **********************************
REM *** Structures ***
REM **********************************
REM *** Elevator details ***
TYPE ElevatorType
ID AS INTEGER
startsquare AS INTEGER
endsquare AS INTEGER
floors AS INTEGER `(-ve for downwards)
zord AS INTEGER
ENDTYPE
REM **********************************
REM *** Global variables ***
REM **********************************
GLOBAL DIM lifts(27) AS ElevatorType
GLOBAL DIM board(100) AS INTEGER
GLOBAL playerat AS INTEGER
REM **********************************
REM *** Constants ***
REM **********************************
REM *** Object constants ***
#CONSTANT boardobj 1
#CONSTANT playerobj 2
#CONSTANT diceobj 3
#CONSTANT firstliftobj 4 `27 lifts IDs 4 - 30
REM **********************************
REM *** Main game logic ***
REM **********************************
SetUpGame()
REPEAT
thrown = RollDice()
MovePlayer(thrown)
UNTIL playerat = 100
EndScreen()
END
REM **********************************
REM *** Level 1 routines ***
REM **********************************
FUNCTION SetUpGame()
InitialiseData()
InitialiseVisuals()
ENDFUNCTION
FUNCTION RollDice()
ENDFUNCTION 1
FUNCTION MovePlayer(thrown)
ENDFUNCTION
FUNCTION EndScreen()
ENDFUNCTION
REM **********************************
REM *** Level 2 routines ***
REM **********************************
FUNCTION InitialiseData()
ENDFUNCTION
FUNCTION InitialiseVisuals()
ENDFUNCTION
Activity 42.2
Adding InitialiseData()
The main tasks of the InitialiseData() function involves us setting up the contents
of the lifts and board arrays, but we'll also need to specify the player's starting
position and seed the random number generator. So the routine can be coded as:
FUNCTION InitialiseData()
InitialiseLifts()
InitialiseBoard()
REM *** Start player at square 1 ***
playerat = 1
REM *** Seed random number generator***
RANDOMIZE TIMER()
ENDFUNCTION
Activity 42.3
Add test stubs for the two new routines in a level 3 section of the program.
Adding InitialiseLifts()
We need a great deal of data to initialise the lifts array since we have to set up
information about each lift. The easiest way to set these details up is to use DATA
statements. These values can then be read into the appropriate part of the array.
FUNCTION InitialiseLifts()
REM *** Lifts start at square ***
DATA 10,13,18,20,24,25,30,34,35
DATA 39,40,49,50,53,54,59,60,65
DATA 68,70,72,75,80,83,87,90,97
REM *** Lifts end at square ***
DATA 11,33,43,21,64,56,31,27,55
DATA 19,41,12,51,48,67,79,61,76
DATA 88,71,52,95,81,58,74,91,77
REM *** Floors covered by lifts ***
DATA 1,2,3,1,4,3,1,-1,2
DATA -2,1,-3,1,-1,1,2,1,1
DATA 2,1,-2,2,1,-3,-1,1,-2
REM *** z-ordinates of lifts' position ***
DATA 47,23,-27,-47,-17,-7,47,13,3
DATA -37,-47,33,47,23,13,-37,-47,-7
DATA 23,47,33,3,-47,-27,13,47,-17
REM *** Store lift IDs (firstliftobj to firstliftobj + 26)***
FOR c = 1 TO 27
lifts(c).ID = firstliftobj + c-1
NEXT c
Adding InitialiseBoard()
In the board array, each cell represents one square on the board. If a square does
not contain the starting position of an elevator, we store zero in the corresponding
element of board; if a square does contain an elevator, then the corresponding cell
in board contains the elevator's number.
We have two choices when storing an elevator number; we can either store the
elevator's ID or we can store the position of that elevator's details in the lifts array.
For example, square 10 on the board has an elevator (ID 4) taking the player to
square 11, so we could either write
board(10) = 4
board(10) = 1
meaning that the elevator starting at square 10 has its details stored in lifts(1).
In fact, the second option is the better approach since it allows more flexibility
should we ever need to change the ID values used for the elevator objects.
FUNCTION InitialiseBoard()
REM ***Square empty (0) or elevator's location in lifts ***
DATA -1,-1,-1,-1,-1, -1,-1,-1,-1,1
DATA -1,-1,2,-1,-1, -1,-1,3,-1,4
DATA -1,-1,-1,5,6, -1,-1,-1,-1,7
DATA -1,-1,-1,8,9, -1,-1,-1,10,11
DATA -1,-1,-1,-1,-1, -1,-1,-1,12,13
The rather strange code needs DATA -1,-1,14,15,-1, -1,-1,-1,16,17
explaining; storing 0's in the DATA -1,-1,-1,-1,18, -1,-1,19,-1,20
DATA statements seemed to DATA -1,21,-1,-1,22, -1,-1,-1,-1,23
upset the program and so it DATA -1,-1,24,-1,-1, -1,25,-1,-1,26
was necessary to store -1's DATA -1,-1,-1,-1,-1, -1,27,-1,-1,-1
and convert them to zeros! REM *** Store this data in board array ***
FOR c = 1 TO 100
READ board(c)
Activity 42.4
Replace the stubs for InitialiseLifts() and InitialiseBoard() with the code given.
Adding InitialiseVisuals()
Now we're ready to load all the visual elements of the game and position the
cameras. The requirements of this routine can best be described as:
All but the first and last of these tasks will involve a few lines of code, and it is
probably best to create a set of level 3 routines to handle the details. So,
InitialiseVisuals() can be coded as:
FUNCTION InitialiseVisuals()
REM *** Set the screen resolution ***
The standard screen SET DISPLAY MODE 1280,1024,32
resolution is used and the LoadBoard()
ambient light is turned up LoadPlayerCharacter()
since the image was too LoadDice()
dark with the default PositionCameras()
setting. REM *** Increase the ambient light ***
SET AMBIENT LIGHT 50
ENDFUNCTION
Activity 42.5
Replace the stub of InitialiseVisuals() with the code given above and create
new level 3 stubs for the functions it calls.
Activity 42.6
Adding LoadBoard()
There are actually two parts to this routine: first we need to load and resize the board
model, but also we need to position the elevators within the board. This second task
will be performed by a separate routine. The code for LoadBoard() is:
FUNCTION LoadBoard()
REM *** Load, scale and position board ***
LOAD OBJECT "board.x",1
SCALE OBJECT 1, 1000,1000,1000
YROTATE OBJECT 1, 90
POSITION OBJECT 1,5,50,0
REM *** Make black areas invisible ***
REM *** These are the holes in the floor ***
REM *** through which the lifts pass ***
SET OBJECT TRANSPARENCY 1,1
AddElevators()
ENDFUNCTION
Adding AddElevators()
This routine loads the elevator model (lift.dbo) and creates another 26 clones of the
model. The data in the board and lifts arrays is then used to position and resize each
elevator.
FUNCTION AddElevators()
REM *** Create the first lift ***
LOAD OBJECT "lift.dbo",firstliftobj
REM *** Create others as clones ***
FOR c = 2 TO 27
CLONE OBJECT lifts(c).ID,firstliftobj
NEXT c
REM *** Position all the lifts ***
FOR c = 1 TO 27
REM *** Rotate lift and scale to match floors ***
liftID = lifts(c).ID
YROTATE OBJECT liftID,-90
Adding LoadPlayerCharacter()
This routine needs to load the appropriate model, scale and rotate it. The code is:
FUNCTION LoadPlayerCharacter()
REM *** Load, scale and position player's character ***
LOAD OBJECT "H-android-move.x",playerobj
SCALE OBJECT playerobj,150,150,150
YROTATE OBJECT playerobj,180
POSITION OBJECT playerobj,2,0,-45 `square 1
REM *** Set the animation play speed ***
SET OBJECT SPEED playerobj,15
ENDFUNCTION
Notice that the routine takes the trouble to check the position of the player on the
board and rotates the character accordingly. This may seem unnecessary since the
player will obviously start at square 1 - however, while testing the program we may
want to start the player at other positions.
Adding LoadDice()
Like LoadPlayerCharacter(), LoadDice() involves loading and scaling a model:
FUNCTION LoadDice()
REM *** Load, scale and position dice ***
LOAD OBJECT "dice.dbo",diceobj
POSITION OBJECT diceobj,30,15,45
SCALE OBJECT diceobj,50,50,50
ENDFUNCTION
Adding PositionCameras()
Two cameras are used in the game. Camera 1 follows the player's character as it
moves through the corridors of the board and forms the main image on the screen;
camera 2 points at the dice and its output is shown as a picture-in-picture window
at the top right of the screen. The code for this routine is:
FUNCTION PositionCameras()
AUTOCAM OFF
REM *** Set up player's camera ***
MAKE CAMERA 1
SET CAMERA VIEW 1, 0,0,1279,1023
POSITION CAMERA 1,3,5,-52
POINT CAMERA 1,3,2,-40
REM *** Set up dice's camera ***
REM **********************************
REM *** Global variables ***
REM **********************************
GLOBAL DIM Lifts(27) AS ElevatorType
GLOBAL DIM board(100) AS INTEGER
GLOBAL playerat AS INTEGER
REM **********************************
REM *** Constants ***
REM **********************************
REM *** Object constants ***
#CONSTANT boardobj 1
#CONSTANT playerobj 2
#CONSTANT diceobj 3
#CONSTANT firstliftobj 4 `27 lifts IDs 4 - 30
REM **********************************
REM *** Main game logic ***
REM **********************************
SetUpGame()
REPEAT
thrown = RollDice()
MovePlayer(thrown)
UNTIL playerat = 100
EndScreen()
END
REM **********************************
REM *** Level 1 routines ***
REM **********************************
FUNCTION SetUpGame()
InitialiseData()
InitialiseVisuals()
ENDFUNCTION
FUNCTION RollDice()
ENDFUNCTION 1
FUNCTION MovePlayer(thrown)
ENDFUNCTION
FUNCTION EndScreen()
ENDFUNCTION continued on next page
FUNCTION InitialiseVisuals()
SET DISPLAY MODE 1280,1024,32
LoadBoard()
LoadPlayerCharacter()
LoadDice()
PositionCameras()
SET AMBIENT LIGHT 50
ENDFUNCTION
REM **********************************
REM *** Level 3 routines ***
REM **********************************
FUNCTION InitialiseLifts()
REM *** Lifts start at square ***
DATA 10,13,18,20,24,25,30,34,35
DATA 39,40,49,50,53,54,59,60,65
DATA 68,70,72,75,80,83,87,90,97
REM *** Lifts end at square ***
DATA 11,33,43,21,64,56,31,27,55
DATA 19,41,12,51,48,67,79,61,76
DATA 88,71,52,95,81,58,74,91,77
REM *** Floors covered by lifts ***
DATA 1,2,3,1,4,3,1,-1,2
DATA -2,1,-3,1,-1,1,2,1,1
DATA 2,1,-2,2,1,-3,-1,1,-2
REM *** z-ordinate of lift's position ***
DATA 47,23,-27,-47,-17,-7,47,13,3
DATA -37,-47,33,47,23,13,-37,-47,-7
DATA 23,47,33,3,-47,-27,13,47,-17
REM *** Store lift IDs (firstliftobj to firstliftobj + 26)***
FOR c = 1 TO 27
lifts(c).ID = c + firstliftobj - 1
NEXT c
REM *** Store start square ***
FOR c = 1 TO 27
READ sq
lifts(c).startsquare = sq
NEXT c
REM *** Store end square ***
FOR c = 1 TO 27
READ fin
lifts(c).endsquare = fin
NEXT c
REM *** Store floors covered ***
FOR c = 1 TO 27
READ fl
lifts(c).floors = fl
NEXT c
REM *** Store z-ord of lift ***
FOR c = 1 TO 27
READ z
lifts(c).zord = z
NEXT c
ENDFUNCTION
continued on next page
FUNCTION LoadBoard()
REM *** Load, scale and position board ***
LOAD OBJECT "board.x",1
SCALE OBJECT 1, 1000,1000,1000
YROTATE OBJECT 1, 90
POSITION OBJECT 1,5,50,0
REM *** Make black areas invisible ***
REM *** These are the holes in the floor ***
REM *** through which the lifts pass ***
SET OBJECT TRANSPARENCY 1,1
AddElevators()
ENDFUNCTION
FUNCTION LoadPlayerCharacter()
REM *** Load, scale and position player's character ***
LOAD OBJECT "H-android-move.x",playerobj
SCALE OBJECT playerobj,150,150,150
YROTATE OBJECT playerobj,180
POSITION OBJECT playerobj,2,0,-45 `square 1
REM *** Set the animation play speed ***
SET OBJECT SPEED playerobj,15
ENDFUNCTION
FUNCTION LoadDice()
REM *** Load, scale and position dice ***
LOAD OBJECT "dice.dbo",diceobj
POSITION OBJECT diceobj,30,15,45
SCALE OBJECT diceobj,50,50,50
ENDFUNCTION
FUNCTION PositionCameras()
AUTOCAM OFF
REM *** Set up player's camera ***
MAKE CAMERA 1
SET CAMERA VIEW 1, 0,0,1279,1023
POSITION CAMERA 1,3,5,-52
POINT CAMERA 1,3,2,-40
REM *** Set up dice's camera ***
MAKE CAMERA 2
POSITION CAMERA 2,50,43,0
POINT CAMERA 2,30,15,45
SET CAMERA FOV 2, 10
SET CAMERA VIEW 2,1100,20,1220,120
ENDFUNCTION
Activity 42.7
Adding RollDice()
With all the level 4, 3 and 2 functions now coded, we can return to the level 1
routines. The next of these to be coded is the RollDice() function which needs to
show the dice being rolled (this appears in camera 2's output) and also returns the
number shown on the dice.
Making what the dice shows on its top surface match the value returned by this
function adds to the complexity of the code.
FUNCTION RollDice()
REM *** Make dice spin ***
FOR c = 0 TO 100
PITCH OBJECT UP diceobj,15`RND(15)+1
TURN OBJECT LEFT diceobj,15`RND(15)+1
ROLL OBJECT LEFT diceobj,15 `RND(15)+1
WAIT 1
NEXT c
REM *** Generate the value to be returned ***
value = RND(5)+1
REM *** make the dice show the value generated ***
SELECT value
CASE 1
ROTATE OBJECT diceobj,-90,0,0
ENDCASE
CASE 2
ROTATE OBJECT diceobj,180,0,0
ENDCASE
CASE 3
ROTATE OBJECT diceobj,0,0,-90
Activity 42.8
In your program, replace the RollDice() test stub with the code given above.
Adding MovePlayer()
Now we come to the main routine of the program. The task it has to do is quite
simple in concept; move the player forward a number of squares corresponding to
the value thrown on the dice.
This time, rather than give you the completed routine, we'll build it up step-by-step,
making it more sophisticated as we go. We'll start by just trying to get the movement
along the bottom row correct. A first attempt at the logic required might be:
Replace the MovePlayer() test stub with the code given. How good are the
results?
The biggest problem is that the character does not move smoothly. To deal with
this, we need to move the model using smaller steps.
to
Activity 42.10
Make the changes described above and test your program again.
The next obvious problem is that the camera has been left behind when it should be
following the character.
This is easily solved by moving the camera in unison with the character. The
character was originally placed at position (2,0,-45) and camera 1 at (3,5,-52), so
to keep the same relative positions, the camera needs to maintain a distance of
(1,5,-7) from the character. This can be achieved using the line:
Activity 42.11
Add the above line at an appropriate point in your MovePlayer() routine and
test the program.
Use elevator
ENDIF
We can handle these two situations if we rework the logic of MovePlayer() slightly,
changing it from:
ENDFOR
to
Use elevator
ELSE
Add 1 to playerat
ENDIF
Move character one square
ENDFOR
Notice that playerat is now incremented a square at a time and only when the
elevator is not being used (using the elevator will handle the updating of playerat
separately). This lets us know where the character is at any time during its move.
Notice also that we check for end of level before moving the character or
incrementing playerat. If you think about it, you'll see that this handles both the
situations of stopping at the end of a level without using the elevator and using the
elevator on the way to a later square during a move.
If we implement checking for the end of level and using the elevator as two new
functions, we can recode MovePlayer() as:
FUNCTION MovePlayer(thrown)
REM *** Animate the character ***
LOOP OBJECT playerobj
REM *** Move character one square at a time ***
FOR squares = 1 TO thrown
REM *** Find out character's current position ***
x# = OBJECT POSITION X(playerobj)
y# = OBJECT POSITION Y(playerobj)
z# = OBJECT POSITION Z(playerobj)
IF AtEndOfLevel()
UseElevator()
ELSE
INC playerat
The function AtEndOfLevel() will return 1 if the character is at the end of a level
(that is on square 10, 20, 30, etc.), otherwise zero will be returned.
Activity 42.12
Being at the end of a level is not the only time when an elevator is used. Should the
character stop on some other square containing an elevator, that evelator must be
taken, moving the character to a new position on the board dependent on the
elevator's destination.
Use elevator
ENDIF
Activity 42.13
Adding UseElevator()
Using the elevator requires several distinct movements of the character. These are
listed below
Ø Line the character up with the elevator. This may involve the character in
moving forward slightly.
Ø Turn the character to face the elevator.
Ø Move the character onto the elevator platform
Ø Move the elevator and character to elevator's destination.
Ø Turn the character away from the elevator.
Ø Move the character to the centre of the new square.
1146 DarkBASIC Pro: Elevators
Ø Face the character in the direction it is to travel.
Ø Return the elevator platform to its original position.
At all stages the camera needs to be tracking the character's movements. Since the
elevator may be used part way through a move, it's probably best to halt the
character's animation at the start of this function and resume it at the end.
The various turning manoeuvres can be handled by a single routine, but for the other
stages, it would probably be best to create a function to implement each step. This
means that UseElevator() can be coded as:
FUNCTION UseElevator()
REM *** Stop character movement ***
STOP OBJECT playerobj
SET OBJECT FRAME playerobj,1
MovePlayerToElevator()
TurnPlayer(-90)
MoveOntoPlatform()
liftnum = MoveElevator()
TurnPlayer(-180)
MoveOffPlatform()
TurnPlayer(90)
ReturnElevator(liftnum)
RepositionCamera()
REM *** Resume character movement ***
LOOP OBJECT playerobj
ENDFUNCTION
Notice that the lift number (liftnum) is returned by MoveElevator(). This value is
later passed to ReturnElevator() to identify which elevator's platform must be
returned to its starting position.
Activity 42.14
Replace your UseElevator() stub with the code given above and create the
appropriate new stubs in the level 3 section of the program.
Note:
MoveElevator() returns the number of the lift being used.
TurnPlayer() takes the angle through which the player is to be turned.
ReturnElevator() takes the number of the lift being returned to
its original position.
Adding MovePlayerToElevator()
We need to get the player's character level with the lift platform before turning the
character to face the lift. We can get the exact position of the lift from the lifts array.
The logic for this routine is:
Wait 50 milliseconds
ENDFOR
Convert the logic given above to DarkBASIC Pro code and replace the
function's stub with your code.
Check that the character moves correctly. (HINT: you may want to add a
WAIT KEY after the function is called in UseElevator(). This will give you a
chance to check out the character movement before the program continues.)
Adding TurnPlayer()
This routine takes a parameter specifying the angle through which the character is
to be turned. For anti-clockwise movement, use a negative value.
Set stepsize to -1
ELSE
Set stepsize to 1
ENDIF
degree o
Rotate character to about its y-axis
Wait 1 millisecond
ENDFOR
Activity 42.16
Create the TurnPlayer() function using the logic given and test it out in your
program. (HINT: you may want to move the WAIT KEY you added
previously.)
Adding MoveOntoPlatform()
With the character facing in the correct direction, we now need to move it onto the
elevator's platform. The logic required for this is:
Wait 50 millisecond
ENDFOR
Activity 42.17
Adding MoveElevator()
This routine moves the platform and the character standing on it to a new square.
The routine must be general enough so that it will work for elevators of different
Since the elevator platform is a limb, its movement will be specified as an offset
from its original position within the model. The model has the platform at the bottom
for lifts that travel upwards (that is, it starts with an offset of zero along the y-axis).
The platform will be at the top when the offset is changed to 100 (the models have
an original height of 100 units). Downward travelling elevators start with an offset
of 100 and this changes to zero to make the platform descend.
Get the lift's position in the lifts array from the board array
Set liftdirection to 1
ELSE
Set liftdirection to -1
ENDIF
ELSE
Set liftendoffset to 0
ENDIF
Wait 10 milliseconds
ENDFOR
Activity 42.18
Add a MoveElevator() function based on the logic given. Test the program and
ensure that the platform moves correctly.
Adding MoveOffPlatform()
To move the character off the platform, after having been made to face in the
appropriate direction, it is just of matter of walking the character forward. This
routine needs the following logic:
Wait 50 millisecond
ENDFOR
Activity 42.19
Add a MoveOffPlatform() function based on the logic given. Test the program
and ensure that the character walks off the platform.
Calculate the height of the lift (negative for a lift that has moved down)
Set liftdirection to -1
ELSE
Set liftdirection to 1
ENDIF
IF liftdirection = -1THEN
Set liftendoffset to 0
ELSE
ENDIF
WAIT 10 milliseconds
ENDFOR
Activity 42.20
Add a ReturnElevator() function based on the logic given. Test the program
and ensure that the platform returns to its original position.
Adding RepositionCamera()
With the character on a new level, we need to move the camera behind it, looking
down the new corridor.
FUNCTION RepositionCamera()
REM *** Get position of character ***
x# = OBJECT POSITION X(playerobj)
# = OBJECT POSITION Y(playerobj)
# = OBJECT POSITION Z(playerobj)
REM *** Position and point camera ***
POSITION CAMERA 1, x#+1,y#+5,z#+7
POINT CAMERA 1,x#+1,y#+2,z-5
ENDFUNCTION
Activity 42.21
Add the routine given above and test out the program.
We can either write a whole new set of routines to handle movement in this
direction, or we can make the previous routines more general so that they can handle
travel in both directions. This second method is the best approach since the changes
required are actually quite small.
We'll need a routine that can give us an indication of which direction the character
is travelling in:
FUNCTION FindDirectionOfTravel()
REM *** IF odd floor,1 else -1 ***
IF ((playerat-1)/10) mod 2 = 0
result = 1
ELSE
result = -1
ENDIF
ENDFUNCTION result
This function returns 1 if the character is on an odd floor and -1 if on an even floor.
Activity 42.22
Fixing RepositionCamera()
As the player's character travels along a corridor it is moving along the z-axis. On
odd corridors (1, 3, 5, etc.) it is moving in a positive direction (its z-ordinate
becoming greater); on even corridors it is travelling in a negative direction. So, if
the camera is to be behind the character it must have a lower z-ordinate than the
character when travelling along odd corridors and a higher z-ordinate when moving
along even corridors (see FIG-42.4).
Camera 1 Camera 1
-z +z
In fact, the camera's z-ordinate should be 7 units less than the character's when
travelling along odd corridors and 7 units more when travelling along even
corridors. The direction in which the camera is pointing should be +5 z-units for
odd corridors and -5 z-units for even corridors.
So, to reposition the camera when we reach a new level, we need to take into account
what level we are on. This requires the following changes to the
FUNCTION RepositionCamera()
REM *** Get position of character ***
x# = OBJECT POSITION X(playerobj)
y# = OBJECT POSITION Y(playerobj)
z# = OBJECT POSITION Z(playerobj)
REM *** IF odd corridor, camera -7 z-units ***
IF FindDirectionOfTravel() = 1
POSITION CAMERA 1, x#+1,y#+5,z#-7
POINT CAMERA 1,x#+1,y#+2,z#+5
ELSE
REM *** Even corridor, +7 z-units ***
POSITION CAMERA 1, x#+1,y#+5,z#+7
POINT CAMERA 1, x#+1,y#+2,z#-5
ENDIF
ENDFUNCTION
Notice that the only difference between the two options is the sign of the 7 and 5
values when calculating the z-ordinates. Since FindDirectionOfTravel() returns 1
or -1 we could rewrite the function without the IF statement as:
FUNCTION RepositionCamera()
REM *** Get position of character ***
x# = OBJECT POSITION X(playerobj)
y# = OBJECT POSITION Y(playerobj)
z# = OBJECT POSITION Z(playerobj)
REM *** Position camera ***
POSITION CAMERA 1, x#+1,y#+5,z# -(7*FindDirectionOfTravel())
POINT CAMERA 1, x#+1,y#+2,z# + (5*FindDirectionOfTravel())
ENDFUNCTION
Activity 42.23
Fixing MovePlayer()
The camera and character are both moved in the MovePlayer() function, so we also
need to take the direction of travel into account in that routine.
FUNCTION MovePlayer(thrown)
REM *** Animate the character ***
LOOP OBJECT playerobj
REM *** Move character one square at a time ***
FOR squares = 1 TO thrown
IF AtEndOfLevel()
REM *** Move to next level ***
UseElevator()
ELSE
REM *** Update character's position ***
INC playerat
REM *** Find out character's current position ***
x# = OBJECT POSITION X(playerobj)
y# = OBJECT POSITION Y(playerobj)
z# = OBJECT POSITION Z(playerobj)
REM *** Find direction of travel ***
direction = FindDirectionOfTravel()
FOR move# = 1 TO 10 STEP 0.5
POSITION OBJECT playerobj,x#,y#,
Ä
z#+move#*direction
POSITION CAMERA 1,x#+1, y#+5,
Ä
z#+(move#-7*direction)
Activity 42.24
Fixing UseElevator()
When the character uses an elevator, the angle of turn depends on which direction
the character is facing, so we need to modify the parameter of the calls to
TurnPlayer() in the UseElevator() function. Both calls should be re-coded as:
TurnPlayer(-90*FindDirectionOfTravel())
Activity 42.25
Fixing MovePlayerToElevator()
How the player moves in line with the elevator's platform is also dependent on the
direction in which the character is travelling. In MovePlayerToElevator() the line
FOR z# = OBJECT POSITION Z(playerobj) TO
Älifts(board(playerat)).zord STEP 0.2
must be changed to
FOR z# = OBJECT POSITION Z(playerobj) TO
Älifts(board(playerat)).zord STEP 0.2*FindDirectionOfTravel()
Activity 42.26
Fixing MoveElevator()
The camera moves in synchronisation with the elevator, but which side of the
character the camera is on depends on the floor on which the elevator starts its
journey, so MoveElevator() must have the line
Activity 42.27
Adding EndGame()
Only one routine left to code! The EndGame() function will delete all objects on
the screen and then show a Rotating 3D version of the text "GAME OVER" for 5
seconds before the program terminates.
FUNCTION EndScreen()
REM *** Delete all existing objects ***
DELETE OBJECTS 1,30
REM *** Load and position text model ***
LOAD OBJECT "endtext.x",1
POSITION OBJECT 1, 0,0,100
REM *** Rotate text for 5 seconds ***
now = TIMER()
WHILE TIMER() - now < 5000
PITCH OBJECT UP 1,1
ENDWHILE
ENDFUNCTION
Activity 42.28
Add the EndGame() code to your program and test the completed game.
Game Review
We've created the most complex code so far - but have we created a game?
There's certainly not a great deal of player interaction in this version of the game
and there are no skills involved in achieving the game's goal, but we could do things
to improve it.
For a start we could have multiple players and then there is at least someone to
compete against.
Rather than use the dice to decide the number of squares moved, we could ask the
player up to six questions. Movement would then be based on the number of correct
questions.
We could add some animated models on some of the squares. These need not have
any purpose but would add to the overall eye candy effect.
FUNCTION InitialiseVisuals()
Activity 42.2 ENDFUNCTION
After a few moves the character moves off the board! FUNCTION MovePlayerToLift()
ENDFUNCTION
Activity 42.13
The expression required is:
Activity 42.15
The code for MovePlayerToElevator() is:
board(playerat) <> 0
FUNCTION MovePlayerToLift()
Ä
NEXT move
0.2*FindDirectionOfTravel() playerat = lifts(liftnum).endsquare
POSITION OBJECT playerobj, x#,y#,z# ENDFUNCTION liftnum
WAIT 50
NEXT z# FUNCTION MoveOffPlatform()
STOP OBJECT playerobj LOOP OBJECT playerobj
SET OBJECT FRAME playerobj,1 x# = OBJECT POSITION X(playerobj)
ENDFUNCTION y# = OBJECT POSITION Y(playerobj)
z# = OBJECT POSITION Z(playerobj)
FUNCTION TurnPlayer(angle) FOR newx# = x# TO x#+5 STEP 0.1
IF angle < 0 POSITION OBJECT playerobj, newx#,y#,z#
stepsize = -1 WAIT 1
ELSE NEXT moves
stepsize = 1 STOP OBJECT playerobj
ENDIF SET OBJECT FRAME playerobj,1
startangle = OBJECT ANGLE Y(playerobj) ENDFUNCTION
FOR degree = startangle TO startangle
Ä+ angle STEP stepsize FUNCTION ReturnElevator(liftnum)
YROTATE OBJECT playerobj, degree liftID = lifts(liftnum).ID
WAIT 1 height = lifts(liftnum).floors*10
NEXT angle IF height > 0
ENDFUNCTION liftdirection = -1
ELSE
FUNCTION MoveOntoPlatform() liftdirection = 1
LOOP OBJECT playerobj ENDIF
x# = OBJECT POSITION X(playerobj) IF liftdirection = -1
y# = OBJECT POSITION Y(playerobj) liftendoffset = 0
z# = OBJECT POSITION Z(playerobj) ELSE
FOR newx# = x# TO x#-5 STEP -0.1 liftendoffset = 100
POSITION OBJECT playerobj, newx#,
Ä
ENDIF
y#,z# liftstartoffset = 100 - liftendoffset
WAIT 50 unitsperperc# = height / 100.0
NEXT moves FOR move = liftstartoffset TO
STOP OBJECT playerobj Äliftendoffset STEP liftdirection
SET OBJECT FRAME playerobj,1 OFFSET LIMB liftID,2,0,move,-2
ENDFUNCTION WAIT 10
NEXT move
FUNCTION MoveElevator() ENDFUNCTION
REM *** Get lift's position in lifts ***
liftnum = board(playerat) FUNCTION RepositionCamera()
REM *** Get lift's ID *** REM *** Get position of character ***
liftID = lifts(liftnum).ID x# = OBJECT POSITION X(playerobj)
REM *** Calc number of world units to be
Ä
y# = OBJECT POSITION Y(playerobj)
moved *** z# = OBJECT POSITION Z(playerobj)
height = lifts(liftnum).floors*10 REM *** IF odd corridor,
REM ***Determine direction (up/down)*** Äcamera -7 z-units ***
IF height > 0 IF FindDirectionOfTravel() = 1
liftdirection = 1 POSITION CAMERA 1, x#+1,y#+5,z#-7
ELSE POINT CAMERA 1,x#+1,y#+2,z#+5
liftdirection = -1 ELSE
ENDIF REM *** Even corridor, +7 z-units ***
REM *** Get char position *** POSITION CAMERA 1, x#+1,y#+5,z#+7
playerx# = OBJECT POSITION X(playerobj) POINT CAMERA 1, x#+1,y#+2,z#-5
playery# = OBJECT POSITION Y(playerobj) ENDIF
playerz# = OBJECT POSITION Z(playerobj) ENDFUNCTION
REM *** Calc player's move per offset
Ä
unit *** REM **********************************
incmove# = height/100.0 REM *** Level 4 ***
REM *** IF lift up, final offset 100 *** REM **********************************
FUNCTION FindDirectionOfTravel()
REM *** IF odd floor,1 else -1 ***
IF ((playerat-1)/10) mod 2 = 0
result = 1
ELSE
result = -1
ENDIF
ENDFUNCTION result
Storing the polygons that go to make up a complex 3D model in this way makes
the computer's task of culling and rendering much easier, since it can use the
information about polygon ordering that is held in the BSP structure to efficiently
determine which polygons have to be drawn onto the screen and which are hidden
behind other polygons and hence may be ignored.
Although BSP files contain the basic geometry of a world, it does not contain other
necessary details such as the images used to texture surfaces. These will be held in
separate files.
Often BSP files are stored in an archive file (that is, a file which contains a collection
of other files. ZIP and RAR files are also archive files). By using an archive file, all
the files required by the model can be stored as a single entity and extracted when
required. Such archive files have the extension .PK3.
DarkBASIC Pro has the ability to load files of this format because several major
games make use of them, notably Quake and Half-Life, to build the world in which
the player is immersed.
You may hear certain terms, such as world, map, or level, used to describe these
complex models.
It is possible to use the level editor which comes with a commercial game to create
your own playing environment and then import the resulting BSP file into your
DarkBASIC Pro program.
In the dialog box that the application creates, enter the name of the .X file to be
converted (see FIG-43.2).
FIG-43.2
Specifying the File to be
Converted
This program creates only the BSP file, not the archive file containing related texture
images used by the model.
For example, if the file world.pk3 contains the file castle.bsp then, assuming the
file is in the current project's folder, we could load the castle model using the line:
The castle model will be loaded and the textures, which should be in the world.pk3
file, will be applied automatically to the model.
When a BSP file has not been archived within a PK3 file, then pk3filename should
be an empty string. For example, if the BSP file dungeon.bsp is in the current
project's folder, we could load the model using the line:
LOAD BSP "","dungeon.bsp"
With no PK3 file to search for the texture images, DarkBASIC Pro will look for
those images in the current directory. So, if the model in dungeon.bsp uses texture
images cobblesstones.jpg and stonewall.jpg, these two files must be in the current
project folder along with dungeon.bsp.
A loaded BSP model cannot be repositioned, resized, or rotated. Only a single BSP
file can be loaded.
The program in LISTING-43.1 loads the BSP file that comes in one of the examples
packaged with DarkBASIC Pro.
LISTING-43.1
REM *** Main section ***
Loading a BSP Model ScreenSetUp()
FUNCTION ScreenSetUp()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(255,255,100)
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,10,-30
POINT CAMERA 0,0,0
ENDFUNCTION
The ikzdm1.bsp file can be found in DarkBASIC Pro's Dark Basic Software\Dark
Basic Professional\Help\Tutorials\World folder.
Before you try running the program, copy the BSP file ikzdm1.bsp, and the
textures it requires, CYGRASS.bmp and CYWALLT.bmp, into the project's
folder.
In the diagram:
Activity 43.2
What happens this time when you attempt to move the camera through the
wall (approach the wall at an angle, rather than straight on)?
Find out how the camera reacts when response is set to -1 and 0.
In the diagram:
The program in LISTING-43.2 shows a sphere hitting the courtyard floor for each
possible value of response.
LISTING-43.2 ScreenSetUp()
REM *** Load BSP ***
BSP/Object Collision LOAD BSP "","ikzdm1.bsp"
REM *** Create sphere ***
MAKE OBJECT SPHERE 1, 10
FOR response = -1 TO 1
POSITION OBJECT 1, -10,20,-5
POINT OBJECT 1,0,-5,0
SET BSP OBJECT COLLISION 1,1,2,response
time = TIMER()
WHILE TIMER() - time < 7000
MOVE OBJECT 1, 1
ENDWHILE
NEXT c
REM *** End Program ***
END
FUNCTION ScreenSetUp()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(255,255,100)
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,35,-80
POINT CAMERA 5,0,0
ENDFUNCTION
Activity 43.3
Activity 43.4
The model should have a collision radius of 2 and implement sliding collision.
Point the alien at (-50,-32,100) and start it walking. Keep the camera on the
model at all times.
, camno , x , y , z
In the diagram:
Since the radius of the collision volume can be adjusted separately in all three
dimensions, it need no longer be spherical in shape. For example, we could change
the collision volume for camera zero to an ovoid shape using the line:
FIG-43.7
SET BSP OBJECT COLLISION RADIUS collindex
The SET BSP OBJECT
COLLISION RADIUS
Statement
, objno , x , y , z
height
Offsetting the collision sphere is achieved using the SET BSP COLLISION
HEIGHT ADJUSTMENT statement which has the format given in FIG-43.9.
FIG-43.9
The SET BSP COLLISION HEIGHT ADJUSTMENT Statement
In the diagram:
For example, if we had previously set the camera's collision details using the line
we could move the camera's collision sphere far below ground level using the line
Activity 43.5
Add the above statement to your program bsp01.dbpro (after the SET BSP
CAMERA POSITION statement), and check that the camera can again move
through the walls. Remove the statement after testing the program.
In the diagram:
In the diagram:
Activity 43.6
integer
In the diagram:
FIG-43.13
X
The BSP COLLISION
Statement
BSP COLLISION Y ( collindex )
real
In the diagram:
In the diagram:
Once this statement has been executed a new BSP model may be loaded.
{
FIG-43.16
ON
The SET BSP SET BSP MULTITEXTURING
MULTITEXTURING
Statement
OFF
Summary
l Binary Space Partitioning (BSP) is a method of storing 3D models designed to
minimise the number of polygons that have to be processed when recreating a
3D model on the screen.
l BSP files and the texture they use are often archived in a PK3 file.
l DarkBASIC Pro comes with a utility for converting .X files to BSP files.
l BSP files are commonly used for creating level, map or world models.
l Use SET BSP CAMERA COLLISION to stop the camera moving through a
BSP model.
l Use SET BSP OBJECT COLLISION to stop another 3D object passing through
a BSP model.
l Use SET BSP CAMERA COLLISION RADIUS to set the radius of a camera's
collision sphere.
l Use SET BSP OBJECT COLLISION RADIUS to set the radius of an object's
collision sphere.
l Use BSP COLLISION HIT to detect if a camera or object has collided with the
BSP model.
l Use SET BSP CAMERA to convert additional cameras for use with a BSP
model.
Notice how sound effects have been added to increase realism. A simple trick has
been added to give the impression of the bullet being vapourised as it hits the wall.
The Program
The main logic of the program is:
Set up screen
Initialise game
DO
Control player
IF BulletFired THEN
Move bullet
ENDIF
LOOP
SetUpScreen()
InitialiseGame()
DO
ControlPlayer()
IF BulletFired()
MoveBullet()
ENDIF
LOOP
END
InitialiseGame This routine loads all the BSP maps, models, and
sounds. It also locks the gun to the screen giving
the typical FPS effect.
SetUpScreen()
InitialiseGame()
DO
ControlPlayer()
IF BulletFired()
MoveBullet()
ENDIF
LOOP
END
FUNCTION SetUpScreen()
REM *** Set screen resolution ***
SET DISPLAY MODE 1280,1024,32
BACKDROP OFF
REM *** Hide mouse ***
HIDE MOUSE
REM *** Position camera ***
AUTOCAM OFF
POSITION CAMERA 2,2,2
POINT CAMERA 0,0,0
ENDFUNCTION
FUNCTION InitialiseGame()
REM *** Load BSP map ***
LOAD BSP "","ikzdm1.bsp"
REM *** Set up collision parameters ***
SET BSP CAMERA COLLISION 1,0,1,0
REM *** Load the weapon ***
LOAD OBJECT "H-Pistol-Static.x",gunobj
REM *** Position object in front of camera ***
LOCK OBJECT ON gunobj
SCALE OBJECT gunobj,300,300,300
POSITION OBJECT gunobj,0,-1,2
REM *** Create hidden object to use as bullet ***
MAKE OBJECT SPHERE bulletobj,0.1
SET BSP OBJECT COLLISION bulletobj,2,0.1,0
HIDE OBJECT bulletobj
REM *** Load sound file ***
LOAD SOUND "gun 2.wav",gunobj
LOAD SOUND "mortar.wav",hitsnd
ENDFUNCTION
FUNCTION ControlPlayer()
REM *** Control camera ***
CONTROL CAMERA USING ARROWKEYS 0,0.5,0.5
REM *** IF Enter pressed, attempt to fire ***
IF INKEY$() = CHR$(13)
FireGun()
ENDIF
ENDFUNCTION
FUNCTION BulletFired()
REM *** Bullet travelling if visible ***
result = OBJECT VISIBLE(bulletobj)
ENDFUNCTION result
FUNCTION MoveBullet()
REM *** Move bullet ***
MOVE OBJECT bulletobj,2
REM *** IF it hits wall,show wall being it ***
IF BSP COLLISION HIT(bulletobj)
ShowBulletHit()
ENDIF
ENDFUNCTION
FUNCTION ShowBulletHit()
REM *** Make bullet larger and brighter ***
SCALE OBJECT bulletobj, 1000,1000,1000
SET OBJECT EMISSIVE bulletobj, RGB(255,255,255)
REM *** Show in this state for 10 millisecs ***
WAIT 10
REM *** Hide bullet and darken ***
HIDE OBJECT bulletobj
SET OBJECT EMISSIVE bulletobj, RGB(100,100,100)
REM *** Return to normal size ***
SCALE OBJECT 2, 100,100,100
REM *** Play hit sound ***
PLAY SOUND hitsnd
ENDFUNCTION
Activity 43.7
ScreenSetUp()
Activity 43.2 REM *** Load BSP ***
LOAD BSP "","ikzdm1.bsp"
ScreenSetUp() SET BSP CAMERA COLLISION 1,0,2,1
REM *** Load BSP *** SET BSP COLLISION HEIGHT ADJUSTMENT 1,-50
LOAD BSP "","ikzdm1.bsp" REM *** Use camera to move around ***
SET BSP CAMERA COLLISION 1,0,2,1 DO
REM *** Use camera to move around *** CONTROL CAMERA USING ARROWKEYS 0,0.1,0.1
DO LOOP
CONTROL CAMERA USING ARROWKEYS 0,0.1,0.1 REM *** End Program ***
LOOP WAIT KEY
REM *** End Program *** END
WAIT KEY
END
Activity 43.6
FUNCTION ScreenSetUp()
SET DISPLAY MODE 1280,1024,32 The main section should be coded as:
COLOR BACKDROP RGB(255,255,100)
BACKDROP ON ScreenSetUp()
AUTOCAM OFF REM *** Load BSP ***
POSITION CAMERA 0,10,-30 LOAD BSP "","ikzdm1.bsp"
POINT CAMERA 0,0,0 SET BSP CAMERA COLLISION 1,0,2,1
ENDFUNCTION SET BSP COLLISION OFF 1
REM *** Use camera to move around ***
This time the camera is stopped when it meets the wall. DO
CONTROL CAMERA USING ARROWKEYS 0,0.1,0.1
Using a value of -1 allows the camera to travel through the LOOP
wall. REM *** End Program ***
WAIT KEY
END
A value of zero creates a sliding collision effect when the
camera comes into contact with the wall.
Activity 43.7
Activity 43.3 No solution required.
No solution required.
Activity 43.4
ScreenSetUp()
REM *** Load BSP ***
LOAD BSP "","ikzdm1.bsp"
REM *** Load Hivebrain ***
LOAD OBJECT "H-Alien Hivebrain-Move.x",1
SCALE OBJECT 1, 200,200,200
POSITION OBJECT 1,0,-32,0
SET BSP OBJECT COLLISION 1,1,2,0
POINT OBJECT 1,-50,-32,100
LOOP OBJECT 1
DO
MOVE OBJECT 1,0.1
POINT CAMERA OBJECT POSITION X(1),OBJECT
POSITION Y(1),OBJECT POSITION Z(1)
LOOP
REM *** End Program ***
END
FUNCTION ScreenSetUp()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(255,255,100)
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA -120,-20,160
POINT CAMERA 100,0,0
ENDFUNCTION
The documented terrain commands are few in number, but several newer statements
which are currently undocumented are also available. We'll start with the
documented statements.
In the diagram:
Since the image used in this example contains only black and white (no shades of
grey) the terrain contours will be flat (black) or at maximum height (white).
The program changes the default light to a point light in order to produce shadows
over the terrain. The position of the camera can be modified using the arrow keys.
SetUpScreen()
FUNCTION SetUpScreen()
REM *** Set display mode required ***
SET DISPLAY MODE 1280,1024,32
REM *** position camera ***
AUTOCAM OFF
POSITION CAMERA 0,100,200,-100
REM *** Change to point light ***
SET POINT LIGHT 0,0,100,500
SET AMBIENT LIGHT 50
ENDFUNCTION
Activity 44.1
In the diagram:
This will free up the RAM space occupied by the terrain object.
If you need to move the terrain elsewhere within the 3D world, you can use the
POSITION TERRAIN statement which has the format shown in FIG-44.5.
FIG-44.5
The POSITION TERRAIN POSITION TERRAIN terno , x , y , z
Statement
In the diagram:
For example, we could move terrain object 1 so that its top-left corner is at position
(200,100,-50) using the statement:
Activity 44.2
Modify your last program to reposition the centre of your terrain to location
(0,0,0).
(HINT: You’ll need to load the contour image as a bitmap and get its
dimensions.)
Reposition the camera so that it is at the centre of the terrain and at a height of
50.
Copy the camera.dba file we created in Chapter 33 into the current project
folder.
{}
X
The TERRAIN
POSITION Statement
TERRAIN POSITION Y ( terno )
real
In the diagram:
For example, to find the coordinates of the top-left corner of terrain object 1, we
could use the statement:
Activity 44.3
In the diagram:
FIG-44.8
A Textured Terrain
The image used will be tiled automatically if necessary. Make sure a seamless image
is used and the tiling will be invisible.
Activity 44.4
real
In the diagram:
For example, we could return the height of position (10,20) within terrain 1 using
the line:
The program in LISTING-44.2 moves a small cube across the terrain (from left to
right). It attempts to keep the cube half buried in the terrain by using the GET
TERRAIN HEIGHT statement. The camera follows the movement.
LISTING-44.2 SetUpScreen()
REM *** Create a terrain object ***
Using the GET MAKE TERRAIN 1,"terra1.bmp"
TERRAIN HEIGHT REM *** Texture terrain ***
Statement LOAD IMAGE "grass.jpg",1
TEXTURE TERRAIN 1,1
REM *** Match terrain coordinates to real coordinates ***
POSITION TERRAIN 1,0,0,511
REM *** Make and position cube ***
MAKE OBJECT CUBE 1,10
POSITION OBJECT 1, 0,0,50
height# = GET TERRAIN HEIGHT(0,0,50)
REM *** Move cube across terrain ***
FOR c = 0 TO 511
REM *** Display current height ***
SET CURSOR 10,10
PRINT "CURRENT HEIGHT :",height#
REM *** Recalculate cube’s height and reposition camera ***
height# = GET TERRAIN HEIGHT(1,c,50)
POSITION OBJECT 1,c,height#,50
POSITION CAMERA c-5,height#+10,0
POINT CAMERA c,height#,50
WAIT 10
NEXT c
REM *** End program ***
WAIT KEY
END
FUNCTION SetUpScreen()
REM *** Set display mode required ***
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(255,255,100)
BACKDROP ON
REM *** position camera ***
AUTOCAM OFF
POSITION CAMERA 0,0,10,-100
REM *** Change to point light ***
SET POINT LIGHT 0,0,100,500
SET AMBIENT LIGHT 50
ENDFUNCTION
real
In the diagram:
The terrain created in this way is a normal 3D object (just like a cube or sphere) and
can be operated on by most 3D statements such as POSITION OBJECT.
By itself, MAKE OBJECT TERRAIN is of little use, since it does not create a visible
terrain. We need to add a few more statements...
In the diagram:
For example, assuming we want to use the file terra1.bmp as our contour file for
terrain 1 and that the file is in the project directory, then we could use the statement:
As you know the pixels that make up an image are represented as a sequence of
numbers. It is these numbers that determine the height of any point on the terrain
object; low numbers give low points, high numbers create high points.
We can override the default sizes and specify a multiplying factor for each
dimension using the SET TERRAIN SCALE statement which has the format given
in FIG-44.14.
In the diagram:
For example, we could double the width, triple the depth and halve the height of
terrain object 1 using the statement:
In the diagram:
For example, we could use the file grain.bmp to create the grain on terrain 1 and
the file texture.bmp to texture it using the lines:
In the diagram:
FUNCTION SetUpScreen()
REM *** Set display mode required ***
SET DISPLAY MODE 1280,1024,32
REM *** position camera ***
AUTOCAM OFF
POSITION CAMERA 0,256,200,256
POINT CAMERA 256,0,256
ENDFUNCTION
Activity 44.6
Make sure the camera.dba file has been copied to the project folder.
If you look closely, you may notice that the texture file has been tiled over the
surface of the terrain. In fact, there's a copy of the image on each square unit of the
terrain.
For example, if our terrain is 512 units square, then we can force a single copy of
the texture image to cover the whole of terrain object 1 using the line:
Activity 44.7
Modify your last program so that a single copy of texture.bmp covers the
whole of the terrain (the terrain is 512 by 512 units).
Notice that by using an inverted texture file, the contours and textures are an exact
match, the yellow in the texture file covering the raised area of the terrain.
In the diagram:
Since no light object is created by this statement, other 3D objects in the scene are
unaffected by the lighting specified.
Activity 44.8
Modify the terrain's lighting in your last program so that dark shading is
created for a white light positioned at (-100,10,250).
If the split is set to 4, then the terrain will be split into a 4 by 4 set of meshes (that
is, 16 meshes). The idea behind this is that as a single mesh the terrain can put a
strain on the processor/video card, but as a set of smaller meshes only those meshes
that are currently on screen need to be processed.
real
In the diagram:
The statement returns a real number representing the y-ordinate value at position
(x,z) on terrain terno. The bottom-left corner of the terrain is taken as position (0,0)
irrespective of the terrain object’s own position within the 3D world.
FUNCTION SetUpScreen()
REM *** Set display mode required ***
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON
REM *** position camera ***
AUTOCAM OFF
POSITION CAMERA 0,0,10,-100
REM *** Increase ambient light ***
SET AMBIENT LIGHT 50
ENDFUNCTION
Activity 44.9
Before the BUILD TERRAIN statement, add a statement which splits the
terrain into 32 by 32 meshes. Does this change have any effect on the speed of
the program?
Z
( terno )
real
In the diagram:
The function returns a real number representing the specified dimension of the
terrain in world units.
For example, to find the width and depth of terrain 1, we would use the lines:
We could use this information to position the centre of the terrain at the origin:
POSITION OBJECT 1, width#/2,0,depth#/2
For example, we could save terrain 1 to a file named myterrain.dbo using the line:
Activity 44.11
In your last program, add code to save the completed terrain to a file called
myterrain.dbo when the Enter key is pressed.
FIG-44.24
The LOAD TERRAIN LOAD TERRAIN filename , terno
Statement
In the diagram:
The loaded terrain will automatically be contoured and textured, assuming the
relevant image files are in place. There is no need to call the BUILD TERRAIN
statement when loading a terrain from a file.
For example, we can load the previously saved myterrain.dbo using the statement:
Activity 44.12
Modify your last program to load the terrain used from a file.
but it will not be recognised as a terrain and therefore none of the terrain-related
statements can be applied to the structure.
If the TERRAIN SPLIT statement has been used to split a terrain into several
separate meshes, these meshes become limbs and it is possible to hide or delete
these limbs.
Activity 44.13
Other limb commands have an effect - but not always the one you might expect!
Summary
Documented Statements
l Use MAKE TERRAIN to create a landscape.
l The contours of the landscape are determined by the image associated with the
terrain object.
l The terrain object's width and depth match that of the image associated with it.
l Place the terrain object within your 3D space using the POSITION TERRAIN
statement.
l Add a texture image to the terrain object using the TEXTURE TERRAIN
statement.
l Determine the height of any point on the terrain object using GET TERRAIN
HEIGHT statement.
l Use SET TERRAIN TILING to specify the size of each texture tile on the
advanced terrain.
l If only one occurrence of the texture image is to be used over the whole terrain,
make the tile size equal to the terrain size.
l Use SET TERRAIN LIGHT to specify the position and colour of an assumed
light source for the terrain.
l Use GET TERRAIN SIZE to find the width and depth of a terrain object.
l A terrain which has been split into limbs can have those limbs manipulated using
standard limb statements.
FIG-44.25
The Island
The aim of the game is to wander about the island terrain until you find the golden
orb.
SetUpGame()
StartGame()
REPEAT
ControlPlayer()
UNTIL treasurereached
EndGame()
END
REM **********************************
REM *** Constants ***
REM **********************************
REM *** Booleans ***
#CONSTANT FALSE 0
#CONSTANT TRUE 1
REM *** Terrain ID ***
#CONSTANT IslandTerrain 1
REM *** Image IDs ***
#CONSTANT IslandTextureImg 1
#CONSTANT IslandGrainImg 2
#CONSTANT SeaImg 3
#CONSTANT OrbImg 4
#CONSTANT TitleImg 5
#CONSTANT FoundImg 6
REM *** 3D Object IDs ***
ID 1 is already used for #CONSTANT OrbObj 2
the terrain object which #CONSTANT SkyBoxObj 3
is considered to be a #CONSTANT OceanObj 4
normal 3D object. REM *** Sprites ***
#CONSTANT TitleSprt 1
#CONSTANT FoundSprt 2
REM **********************************
REM *** Globals ***
REM **********************************
GLOBAL treasurereached = FALSE `Indicates if sphere found
GLOBAL spherex# `Coords of sphere
GLOBAL spherez#
Activity 44.14
Type in the code (treasure.dbpro) given so far and create required Level 1 test
stubs. Press Escape to terminate the program.
Adding StartUpGame()
Starting up the game involves setting the screen resolution, positioning the camera,
and creating the scene, so the logic of this routine can be described as:
Activity 44.15
Write code for the StartUpGame() function. Position camera and Create scene
should be implemented in other functions.
The screen resolution should be set to 1280 by 1024 with 32 bit colour.
Add level 2 test stubs for routines named PositionCamera() and CreateScene().
FUNCTION PositionCamera()
AUTOCAM OFF
POSITION CAMERA -800,5,-600
POINT CAMERA 20,5,100
ENDFUNCTION
Activity 44.16
Adding CreateScene()
Our game world consists of a terrain-constructed island, a sky, an ocean and the
golden orb. Each of these elements will be created by separate routines, so the code
for CreateScene() will be:
FUNCTION CreateScene()
LoadTerrain()
CreateSkyBox()
LoadOcean()
PlaceOrb()
ENDFUNCTION
Activity 44.17
Adding LoadTerrain()
The terrain uses the files shown in FIG-44.26. All images are 1024 by 1024.
In LoadTerrain():
The terrain object should be 1024 by 1024 with a 30% height factor.
Do not allow duplicate tiling when the terrain is textured.
Use POSITION OBJECT to place the centre of the terrain at position
(0,0,0).
Use SET OBJECT TRANSPARENCY IslandTerrain,6 to make the black
areas of the terrain transparent.
Set the ambient light level to 70.
Adding CreateSkyBox()
Back in Chapter 33 we used a sky sphere to create a more realistic scene around our
gallows. However, a cube is often used as an alternative to a sphere when creating
sky and far-off scenery. We have two options when using a box to create our sky.
One option is to create a model containing 6 plane limbs and texture each separately.
We are forced to use planes rather than a cube since it is not possible to texture each
side of a cube with different images. Alternatively, we can create a cube in a 3D
modelling program and add the textures within that program (where we have more
control over what image covers each face of the cube).
Even the images used must be properly prepared if the final effect is to look seamless
and natural.
Luckily, DarkBASIC Pro comes with a skybox in one of its examples, so we'll use
that in this program.
FUNCTION CreateSkyBox()
REM *** Load the skybox ***
LOAD OBJECT "sb.x",SkyBoxObj
REM *** Make it large enough to circle the island ***
SCALE OBJECT SkyBoxObj,20,20,20
REM *** Tweek the texture so no seam shows ***
SET OBJECT TEXTURE SkyBoxObj,2,1
ENDFUNCTION
Activity 44.19
Copy the files required by the skybox to the current project folder. The files
are: sb.x, and sb1.dds to sb6.dds.
Adding LoadOcean()
The base of the skybox doesn't look too ocean-like. One way of fixing this is to
replace the texture used on the base of the skybox (one of the sb.dds files), but
another approach is to hide the skybox base with a slightly higher plane and texture
FUNCTION LoadOcean()
REM *** Create a plane ***
MAKE OBJECT PLAIN OceanObj,4096,4096
Texture the plane ***
LOAD IMAGE "sea.tga",SeaImg
TEXTURE OBJECT OceanObj,SeaImg
REM *** Position the plane ***
XROTATE OBJECT OceanObj,-90
POSITION OBJECT OceanObj,0,2,0
REM *** Make plane transparent ***
SET OBJECT TRANSPARENCY OceanObj,2
ENDFUNCTION
Activity 44.20
Adding PlaceOrb()
The last stage of the game initialisation is placing the golden orb somewhere on the
island. Since we'd like each game to have a different location for the orb, we'll need
to use some random positioning method.
However, we can't choose any position or the orb might end up in the water or buried
in a hillside, so we need to check that the position chosen is suitable.
Since the terrain object occupies from point (-512,-512) - bottom-left corner - to
(512,512) - top-right corner - as measured on the XZ plane, we need to generate
an x-ordinate in the range -512 to +512. The same range is required for the
z-ordinate. This can be achieved using the lines:
x# = RND(1024)-512
z# = RND(1024)-512
From this we can come up with an outline logic for the PlaceOrb() function:
Activity 44.21
Texture orb using From the logic given above, add the code for PlaceOrb() to your project.
gold.bmp
Activity 44.22
Adding ControlPlayer()
This routine will allow the player to take control of the camera using the mouse. As
a first attempt we need only use the PointCameraUsingMouse() and
MoveCameraUsingMouse() routines that we developed in a previous chapter.
However, we must adjust the height of the camera to be slightly above the terrain
it is travelling along.
FUNCTION ControlPlayer()
PointCameraUsingMouse(0)
MoveCamerausingMouse(0,1)
REM *** Lift camera above ground height ***
x# = CAMERA POSITION X(0)
z# = CAMERA POSITION Z(0)
y# = GET TERRAIN GROUND HEIGHT(IslandTerrain,x#,z#)+5
POSITION CAMERA 0,x#,y#,z#
ENDFUNCTION
Activity 44.23
The trouble with this simple version of ControlPlayer() is that the player can walk
on water and climb the steepest hills with ease. A more realistic version of the
routine would limit the the player to the land and stop any steep climbs or descents.
To achieve this, all we need to do is check the camera's new position after each
move and if it is too low (in the water) or is too large a change in the y-ordinate
(ascending/descending steep cliffs), then we can move the camera back to its earlier
position.
FUNCTION ControlPlayer()
REM *** Get camera's current position ***
oldx# = CAMERA POSITION X(0)
oldy# = CAMERA POSITION Y(0)
oldz# = CAMERA POSITION Z(0)
REM *** Move camera ***
PointCameraUsingMouse(0)
MoveCameraUsingMouse(0,1)
REM *** Lift camera above ground height ***
x# = CAMERA POSITION X(0)
z# = CAMERA POSITION Z(0)
y# = GET TERRAIN GROUND HEIGHT(IslandTerrain,x#,z#)+5
POSITION CAMERA 0, x#,y#,z#
REM *** IF too low or large change in height ***
IF (y# < 7.5) OR (ABS(oldy#-y#)>1)
REM *** Return camera to previous position ***
POSITION CAMERA 0, oldx#,oldy#,oldz#
ENDIF
ENDFUNCTION
But even now, the routine is not quite finished. It also needs to detect when the
camera is close to the sphere so that the global variable, treasurefound, can be set
to true. This is done by ending the routine with the lines:
REM *** IF sphere reached, set variable ***
IF CameraBesideOrb()
treasurereached = TRUE
ENDIF
Activity 44.24
Write the code for CameraBesideOrb() which should return 1 if the x and z
ordinates of the camera and sphere are both within 5 units of each other;
otherwise zero is returned.
Adding EndGame()
This function will flash up the message FOUND for 3 seconds before the program
shuts down. The message is created as an animated sprite (see FIG-44.27).
FIG-44.27
The Animated Sprite
Message
FUNCTION EndGame()
REM *** Load sprite ***
Activity 44.25
Add this last routine to your program and test the completed project.
These features are not always included just to help out the keen player; a
programmer will often add features to a game to help with the testing. For example,
testing the island game can be time-consuming if you have no idea where the golden
orb is - or where you are, for that matter.
It would be useful if we knew the coordinates of both the sphere and the camera.
That way, we could find the orb quickly and make sure that the game ends correctly.
In the code that follows, we'll let the player find out the position of the orb by
pressing the "s" key and the position of the camera by hitting "c". To do this we'll
need to change the coding of the main section to read:
SetUpGame()
StartGame()
REPEAT
ControlPlayer()
IF INKEY$()="s"
res$ = STR$(spherex#)+" "+STR$(spherez#)
DisplayMessage(res$,200,200)
ENDIF
IF INKEY$() = "c"
res$ = STR$(CAMERA POSITION X(0))+" "
Ä
+STR$(CAMERA POSITION Z(0))
DisplayMessage(res$,200,200)
ENDIF
UNTIL treasurereached
EndGame()
END
The new function, DisplayMessage(), is one we've used before and is coded as:
REM **********************************
REM *** Debug Routines ***
REM **********************************
FUNCTION DisplayMessage(mes$,x,y)
now = TIMER()
WHILE TIMER() - now < 1000
SET CURSOR x,y
PRINT mes$
ENDWHILE
ENDFUNCTION
Notice the function is placed in a special section for debugging (testing) routines.
Update your project to reflect these changes and use the secret keys to help
locate the orb.
Code added specifically to aid debugging would often be removed from the final
product after testing is complete, but sometimes left in to allow those in the know
to gain an advantage over others (cheat!).
Activity 44.4
Activity 44.8
The main section should be changed to:
To add the lightig effect add the folowing lines to the
#INCLUDE "camera.dba" main section before the BUILD TERRAIN statement:
SetUpScreen()
REM *** Create terrain ***
REM *** Set up terrain lighting ***
MAKE TERRAIN 1,"terrain02.bmp"
SET TERRAIN LIGHT 1, -100,10,250,0
REM *** Reposition terrain to (0,0,0)
POSITION TERRAIN 1, 0, 0, 0
REM *** Find size of terrain ***
LOAD BITMAP "terrain03.jpg",1 Activity 44.9
size = BITMAP WIDTH(1)
DELETE BITMAP 1 The statement required to split the terrain into the
REM *** Move camera to centre *** required number of tiles is:
POSITION CAMERA 0, size/2,50,-size/2
REM *** Texture terrain *** SET TERRAIN SPLIT 1, 32
LOAD IMAGE "grass256.jpg",2
TEXTURE TERRAIN 1,2 The smoothness of the camera movement should
REM *** Allow user to move camera *** improve.
DO
PointCameraUsingMouse(0)
FUNCTION CreateSkyBox()
LOAD OBJECT "sb.x",SkyBoxObj
SCALE OBJECT SkyBoxObj,20,20,20
SET OBJECT TEXTURE SkyBoxObj,2,1
ENDFUNCTION
FUNCTION LoadOcean()
MAKE OBJECT PLAIN OceanObj,4096,4096
LOAD IMAGE "sea.tga",SeaImg
TEXTURE OBJECT OceanObj,SeaImg
XROTATE OBJECT OceanObj,-90
POSITION OBJECT OceanObj,0,2,0
SET OBJECT TRANSPARENCY OceanObj,2
ENDFUNCTION
FUNCTION PlaceOrb()
REM *** Seed random number generator ***
RANDOMIZE TIMER()
REM *** Generate a valid position ***
REPEAT
x# = RND(1024)-512
z# = RND(1024)-512
y# = GET TERRAIN GROUND HEIGHT
Ä
(IslandTerrain,x#,z#)
UNTIL y# > 5
REM *** Create and place sphere ***
MAKE OBJECT SPHERE OrbObj,2
LOAD IMAGE "gold.bmp",OrbImg
TEXTURE OBJECT OrbObj,OrbImg
POSITION OBJECT OrbObj,x#,y#+1,z#
REM *** Record its position ***
spherex# = x#
spherez# = z#
ENDFUNCTION
FUNCTION CameraBesideOrb()
IF ABS(CAMERA POSITION X(0)-spherex#)<5
Ä
AND ABS(CAMERA POSITION Z(0)-spherez#)
Ä
<5
result = 1
ELSE
result = 0
ENDIF
ENDFUNCTION result
REM **********************************
REM *** Debug Routines ***
REM **********************************
FUNCTION DisplayMessage(mes$,x,y)
now = TIMER()
WHILE TIMER() - now < 1000
SET CURSOR x,y
PRINT mes$
ENDWHILE
ENDFUNCTION
DarkBASIC Pro uses the term matrix to describe a two-dimensional plane which
is divided into a number of rows and columns, collectively known as subdivisions.
This creates a grid pattern in the plane (see FIG-45.1).
FIG-45.1
A Flat Matrix
A 2D grid
The grid appears distorted
because of the camera angle
The intersecting subdivisions create a set of rectangular areas and each rectangle is
divided into two triangles.
The y-ordinate of individual apexes which make up the triangles within the structure
can be manipulated to create a 3D effect (see FIG-45.2).
FIG-45.2
Adding a Third
Dimension to the Matrix
A matrix, like any other 3D object, can also have a texture applied to it (see
FIG-45.3) allowing you to create a landscape effect similar to that produced by a
terrain object.
FIG-45.3
Texturing a Matrix
The set of commands available to create and manipulate a matrix are described in
the next section.
ng
nits lo
200 u
deep
Activity 45.1
The grid is initially positioned so that its bottom-left corner is at the origin (0,0,0).
By executing this statement, every corner in the grid will be displaced by a random
amount between 0 and ymax. For example, assuming we began by creating a four
tile matrix grid with the statement
we could randomly reposition the corners of each tile by a value of between 0 and
10.5 using the statement:
The resulting grid will, of course, vary each time the program is run, but a typical
example is shown in FIG-45.7.
FIG-45.7
The corners of
each tile have been
The Effect of the moved randomly up
RANDOMIZE MATRIX the y-axis
Statement
For example, to show any changes to grid 1 we would use the line:
We now have enough knowledge to create our first 3D matrix grid as shown in
LISTING-45.1.
LISTING-45.1 SetUpScreen()
REM *** Create a grid (800 by 720) ***
Creating a Matrix Grid MAKE MATRIX 1,800,720,8,9
REM *** Wait for key press ***
WAIT KEY
REM *** Randomly adjust the y ordinates ***
REM *** to a maximum of 40 ***
RANDOMIZE MATRIX 1,40
REM *** Update matrix ***
UPDATE MATRIX 1
REM *** End program ***
WAIT KEY
END
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
REM *** Position camera for a good view of matrix ***
AUTOCAM OFF
POSITION CAMERA 0, 500,300,-600
ENDFUNCTION
Activity 45.2
Modify the grid to be 1000 by 500 with each rectangle being 10 by 10.
Set the random height variation to range from 0 to 20. Run the program to
check the effect of these changes.
Activity 45.3
Modify your program again so that the camera position can be altered using
the arrow keys. Start with the camera at position (0,100,-300).
Move the camera over the grid to check that the changes are operating
correctly.
In the diagram:
is shown in FIG-45.10.
1
3
2
1
0
X points
Activity 45.4
By using nested FOR loops we can assign specific values to every point in the grid.
For example, the program in LISTING-45.2 creates a rising ramp by increasing the
size of every point as we move from the left to the right of a grid (see FIG-45.11).
FIG-45.11
A Controlled Matrix
Creating a Structured Grid REM *** Set up 200 by 100 grid (20 by 10 tiles) ***
MAKE MATRIX 1,200,100,20,10
REM *** Set initial height ***
height#=0.5
REM *** FOR each x edge DO ***
FOR x = 0 TO 20
REM *** Increase height by 30% ***
height# = height# *1.3
REM *** FOR each z edge DO ***
FOR z = 0 TO 10
REM *** Set edge to specified height ***
SET MATRIX HEIGHT 1,x,z,height#
NEXT z
NEXT x
REM *** Update screen ***
UPDATE MATRIX 1
REM *** Move camera using cursor keys ***
DO
CONTROL CAMERA USING ARROWKEYS 0, 1, 1
LOOP
REM *** End program ***
WAIT KEY
END
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
REM *** Position camera ***
AUTOCAM OFF
POSITION CAMERA 0,50,100,-300
ENDFUNCTION
Activity 45.5
Modify the code so that the ramp effect goes from front to back rather than
side-to-side.
real
In the diagram:
The statement returns a real number representing how high above the base of the
matrix the specified point is. For example, if we have previously executed the lines
The problem can be solved using the GET GROUND HEIGHT statement which
returns the height at any point in the matrix. The statement has the format shown in
FIG-45.14.
FIG-45.14
The GET GROUND GET GROUND HEIGHT ( matno , x , z )
HEIGHT Statement
real
In the diagram:
Remember, there are two main differences between GET MATRIX HEIGHT and
GET GROUND HEIGHT:
The program in LISTING-45.3 displays the height at various positions along the
back edge of a matrix.
LISTING-45.3 SetUpScreen()
REM *** Create a grid (150 by 100) ***
Finding the Matrix Height MAKE MATRIX 1, 150, 100,3,2
at any Point REM *** Set the back corner to a height of 10.5 ***
SET MATRIX HEIGHT 1, 3,2, 10.5
REM *** Update matrix ***
UPDATE MATRIX 1
REM *** Get the height every 10 units ***
REM *** along the back edge of matrix ***
SYNC ON
FOR c = 0 TO 150 STEP 10
SET CURSOR 0, 10
PRINT “Height at (”,c,",",100,") = “,GET GROUND HEIGHT(1,c,100)
SYNC
WAIT 500
NEXT c
REM *** End program ***
WAIT KEY
END
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
REM *** Position camera ***
AUTOCAM OFF
POSITION CAMERA 0,200,300,-600
ENDFUNCTION
Activity 45.6
Change the z-ordinate in the GET GROUND HEIGHT to 70.0 and check out
the values displayed.
ON
matno
In the diagram:
Activity 45.7
FIG-45.16
The MATRIX
WIREFRAME STATE
MATRIX WIREFRAME STATE ( matno )
Statement
real
In the diagram:
This statement returns the value 1 if the matrix is being displayed in wireframe
mode, otherwise zero is returned.
The PREPARE MATRIX TEXTURE statement is used to determine how the image
is to be split up. This statement has the format shown in FIG-45.17.
In the diagram:
For example, let's start by loading the image shown in FIG-45.18 below.
FIG-45.18
A Texture Image for the
Matrix
and then prepare the image, without dividing it into parts, with the line
PREPARE MATRIX TEXTURE 1,1,1,1
then the image will be taken as a single tile. However, if we use the statement
4 5 6
Activity 45.8
What effect does this have when you run your program?
As you can see from the results of the last Activity, the PREPARE MATRIX
TEXTURE statement also applies one of the tiles in the image to every square in
the grid. In the first instance, since the image had not been split into several tiles,
the whole image is applied to each rectangle in the grid. In the second case, where
the image had been split into six tiles, the top-left tile (tile 1) has been applied to
every square, giving a completely red grid.
FIG-45.20
FILL MATRIX matno , height , tileno
The FILL MATRIX
Statement
In the diagram:
This statement can only be used after a PREPARE MATRIX TEXTURE statement
has been executed to set up the image being used by the grid.
For example, we could texture the complete grid with tile 2 from B.bmp and lift the
whole grid to a height of 12.5 units using the lines:
In the diagram:
For example, let’s assume we’ve created a 5 by 3 grid using the statement
then we could place tile 2 of the image in rectangle (4,0) using the statement
4 5 6
Matrix Grid
2 Tile 2 textures
rectangle (4,0)
1
0 1 2 3 4
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 0,200,300,-600
ENDFUNCTION
Activity 45.10
Notice that, as well as loading the specified tile to the required rectangle in the grid,
the remainder of the grid is red. This is because tile 1 is always used as default to
fill the grid after the PREPARE MATRIX TEXTURE statement is executed.
Activity 45.11
Modify your last program so that tile 5 of the same image is placed in
rectangle (2,1).
Modify the program again so that all six image tiles are stored over six
rectangles. The rectangles should be placed as follows:
Tile Rectangle
1 (1,2)
2 (2,2)
3 (3,2)
4 (1,1)
5 (2,1)
6 (3,1)
To spread a single image over the whole grid without repetition of the image, all
you need to do is divide the image up so that it has one tile for every rectangle in
the grid.
tileno = 1
FOR x = 19 TO 0 STEP -1
FOR z = 0 TO 19
SET TEXTURE TILE 1,x,z,tileno
INC tileno
LISTING-45.5 SetUpScreen()
REM *** Create grid 20 by 30 rectangles ***
Spreading a Single Image MAKE MATRIX 1,200,300,20,30
Over the Whole Matrix REM *** Load image and divide into 20 by 30 tiles ***
LOAD IMAGE “F.bmp”,1,0
PREPARE MATRIX TEXTURE 1,1,20,30
REM *** Copy each tile onto the grid starting at back-left ***
tileno = 1
FOR z =29 TO 0 STEP -1
FOR x = 0 TO 19
SET MATRIX TILE 1, x, z, tileno
INC tileno
NEXT x
NEXT z
REM *** Update grid ***
UPDATE MATRIX 1
REM *** Use camera to move about ***
DO
CONTROL CAMERA USING ARROWKEYS 0, 1, 1
LOOP
REM *** End program ***
END
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 0,100,-300
ENDFUNCTION
Activity 45.12
Change the texture image used to grass.jpg and randomly modify the points in
the grid to vary up to 30 units in height.
This is a rather strange command to get your head round, so the program in
LISTING-45.6 demonstrates its use by incrementing the trim factor along the x edge
every time you press a key.
LISTING-45.6 SetUpScreen()
REM *** Set up grid with texture ***
Trimming the Texture MAKE MATRIX 1,40,40,2,2
Image LOAD IMAGE “trimmer.bmp”,1,1
PREPARE MATRIX TEXTURE 1,1,2,2
REM *** Position grid ***
POSITION MATRIX 1, 0,0,0
REM *** Set xtrim value to zero ***
xoff# = 0.0
DO
REM *** Set the trim on the grid ***
SET MATRIX TRIM 1,xoff#,0.0
REM *** Fill every rectangle with same trimmed tile ***
FILL MATRIX 1,0,2
UPDATE MATRIX 1
REM *** Wait for key press ***
WAIT KEY
REM *** Make the offset larger ***
xoff# = xoff# + 0.01
LOOP
REM *** End program ***
END
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 0,100,-300
ENDFUNCTION
Activity 45.13
Type in and test the program given above (matrix06.dbpro), observing how
the texture of the grid changes as the xoff# value is changed.
Modify the program so that both the xtrim and ztrim values are changed.
{
FIG-45.25
UP
The SHIFT MATRIX
Statement
DOWN
SHIFT MATRIX matno
LEFT
RIGHT
In the diagram:
For example, the statement SHIFT MATRIX DOWN 1 has the effect of moving
every rectangle in the matrix one position backwards within the matrix. Hence,
rectangle (0,0) would move to position (0,1). The rectangles currently at the back
of the grid are moved round to the front. All the information in that grid - the height
of its corners and its texture are moved along with the rectangle. The overall effect
is to give the impression that the surface is moving under the camera. The program
in LISTING-45.7 demonstrates this effect.
SetUpScreen()
LISTING-45.7 REM *** Set up grid with texture ***
MAKE MATRIX 1,2000,3000,20,30
Shifting the Matrix LOAD IMAGE “grass.jpg”,2
Texture PREPARE MATRIX TEXTURE 1,2,20,30
tileno = 1
FOR z =29 TO 0 STEP -1
FOR x = 0 TO 19
SET MATRIX TILE 1, x, z, tileno
INC tileno
NEXT x
NEXT z
RANDOMIZE MATRIX 1, 20
REM *** Update grid on screen ***
UPDATE MATRIX 1
REM *** Shift matrix texturing ***
DO
CONTROL CAMERA USING ARROWKEYS 0, 5, 1
REM *** Shift matrix one position ***
SHIFT MATRIX DOWN 1
UPDATE MATRIX 1
WAIT 50
LOOP
REM *** End program ***
END
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 0,100,-300
ENDFUNCTION
Modify the direction of shift to try out each of the other three options.
integer
In the diagram:
For example, if we had prepared an image for matrix 1 with the statement
would display the value 600 (this being the result of 20 x 30).
integer
In the diagram:
}
In the diagram:
The program in LISTING-45.8 creates a matrix and a cube. The cube’s centre is at
position (0,0,0) and exists only to show the position of the 3D origin as the matrix
is moved. The matrix is moved from its original position when the user presses a
key.
LISTING-45.8 SetUpScreen()
Positioning the Matrix REM *** Create grid 20 by 30 rectangles ***
MAKE MATRIX 1,200,300,20,30
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 100,50,-300
ENDFUNCTION
Activity 45.15
FIG-45.29
{
The MATRIX POSITION X
Statement
MATRIX POSITION Y ( matno )
real
In the diagram:
The statement returns a real number representing the position of the bottom-left
corner of the specified matrix. For example, if the bottom-left corner of matrix 1 is
positioned at location (20,30,40) with the statement POSITION MATRIX 1,20,30,40
then the lines
x-ord : 20
y-ord : 30
z-ord : 40
If the mode value is omitted, the matrix is displayed in standard transparency mode;
when included, the mode parameter allows different transparency effects to be
produced.
LISTING-45.9 SetUpScreen()
REM *** Change to point light ***
Creating a Transparent SET POINT LIGHT 0,0,100,500
Matrix REM *** Create first terrain object ***
MAKE TERRAIN 1,"terraintest.bmp"
LOAD IMAGE “grass2.jpg”,1
TEXTURE TERRAIN 1,1,
POSITION TERRAIN 1,0,0,1023
REM *** Set up matrix with water texture ***
MAKE MATRIX 1,900,900,1,1
LOAD IMAGE “030.bmp”,2
PREPARE MATRIX TEXTURE 1,2,1,1
REM *** Position matrix above terrain ***
POSITION MATRIX 1,0,5,0
REM *** Use each transparency mode ***
FOR c = 0 TO 5
GHOST MATRIX ON 1,c
REM *** Update grid on screen ***
UPDATE MATRIX 1
WAIT KEY
NEXT c
REM *** End program ***
WAIT KEY
END
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 550,200,-300
ENDFUNCTION
Activity 45.16
In the diagram:
FIG-45.33
Transparency Issues
LISTING-45.10 SetUpScreen()
REM *** Create a textured, transparent grid ***
Demonstrating Problems MAKE MATRIX 1, 2000, 2000,100,100
with Transparency LOAD IMAGE “grass1.jpg”,1
PREPARE MATRIX TEXTURE 1,1,1,1
RANDOMIZE MATRIX 1, 40
GHOST MATRIX ON 1
UPDATE MATRIX 1
REM *** Embed a cube in the matrix ***
MAKE OBJECT CUBE 1,100
LOAD IMAGE “wall_U5_01.jpg”,2
TEXTURE OBJECT 1,2
POSITION OBJECT 1, 200,-10,200
REM *** Allow user to move camera ***
DO
CONTROL CAMERA USING ARROWKEYS 0,5,1
LOOP
REM *** End program ***
END
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 500,100,-100
ENDFUNCTION
Activity 45.17
Type in the program above (matrix10.dbpro) and observe how the cube is
displayed.
We can force a matrix object to be drawn last by using the SET MATRIX
PRIORITY statement. This will create the desired transparency effect. The SET
MATRIX PRIORITY has the format shown in FIG-45.34.
FIG-45.34
The SET MATRIX
SET MATRIX PRIORITY matno , prtyflg
PRIORITY Statement
In the diagram:
Activity 45.18
just before the MATRIX UPDATE statement and observe what difference this
makes to the display.
Since several triangles within a shape may share the same vertex, a single point may
have many vertex normals. The angle and length of a normal has a direct effect on
how a 3D object is lit. By changing a normal, we create an immediate change to
how light is reflected at that point on the object to which the normal is attached.
The SET MATRIX NORMAL statement allows us to modify the length and
direction of matrix normals. The statement requires the vertex and end point of the
modified normal to be specified. The statement format is shown in FIG-45.36.
In the diagram:
The program in LISTING-45.11 creates a single tile matrix and modifies a vertex
normal each time a key is pressed.
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 100,100,-200
ENDFUNCTION
Activity 45.19
Type in the program above (matrix11.dbpro) and find out what effect the
changes to the normal have on the way the matrix is lit.
What effects are produced by varying the nx, ny, and nz values (use both
positive and negative values)?
In the diagram:
As you can see, there are many parameters to this statement. The best way to make
sense of the effect it has on the matrix grid is to test each possible combination of
parameter values. The program in LISTING-45.12 does exactly that, allowing the
user to cycle through the values for each parameter.
LISTING-45.12 SetUpScreen()
REM *** Create a grid (1000 by 750) ***
Testing the SET MAKE MATRIX 1, 2000, 2000,100,100
MATRIX Statement REM *** Texture matrix ***
LOAD IMAGE “bwtexture.bmp”,1
PREPARE MATRIX TEXTURE 1,1,1,1
REM *** Randomly adjust the y ordinates ***
REM *** to a maximum of 40 ***
RANDOMIZE MATRIX 1, 40
SET MATRIX PRIORITY 1,1
REM *** Update matrix ***
UPDATE MATRIX 1
REM *** Embed a cube in the matrix ***
MAKE OBJECT CUBE 1,100
LOAD IMAGE “bricks.jpg”,2
TEXTURE OBJECT 1,2
POSITION OBJECT 1, 200,-10,200
REM *** Change to point light ***
SET POINT LIGHT 0,0,100,500
REM *** Create fog ***
FOG ON
FOG COLOR RGB(100,100,255)
FOG DISTANCE 1000
REM *** Set up parameter values ***
wire = 0
trans = 0
cull = 0
filter = 0
light = 0
fog = 0
amb = 0
REM *** Allow user to modify settings ***
SYNC ON
DO
REM *** Display current settings ***
SET CURSOR 0,10
PRINT “ 1 - Wireframe toggle (”,wire,")"
PRINT “ 2 - Transparency toggle (”,trans,")"
PRINT “ 3 - Cull Toggle (”,cull,")"
PRINT “ 4 - Filter Increment (”,filter,")"
PRINT “ 5 - Light Toggle (”,light,")"
continued on next page
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 500,100,-100
ENDFUNCTION
FUNCTION GetOption()
result$ =""
REPEAT
result$ = INKEY$()
UNTIL result$ <>""
WHILE INKEY$() <> ""
ENDWHILE
ENDFUNCTION result$
Activity 45.20
integer
In the diagram:
The statement returns 1 if the specified matrix does exist, otherwise zero is returned.
Hence, we might report the non-existence of matrix 1 with the statement:
IF MATRIX EXIST(1) <> 1
PRINT “Matrix does not exist”
ENDIF
Summary
l A matrix object can be used to create a landscape.
l Use RANDOMIZE MATRIX to randomly set the height of each vertex within
a matrix.
l Use UPDATE MATRIX to update a matrix on-screen after changes have been
made.
l Use SET MATRIX HEIGHT to modify the height of individual vertices within
a matrix.
l Use GET MATRIX HEIGHT to discover the height of any vertex on a matrix.
l Use GET GROUND HEIGHT to discover the height on any arbitary point on a
matrix.
l Use FILL MATRIX to texture the matrix with a specified section of the image
specified in PREPARE MATRIX TEXTURE.
l Use MATRIX TILE COUNT to discover how many sections a prepared image
has been split into.
l Use MATRIX TILES EXIST to check that an image has been prepared for a
matrix.
l Use SET MATRIX PRIORITY to modify the order in which matrices are drawn
on the screen.
REM *** Position camera *** REM *** End program ***
AUTOCAM OFF WAIT KEY
POSITION CAMERA 0, 500,300,-600 END
REM *** Create a grid (1000 by 500) *** REM *** Update matrix ***
MAKE MATRIX 1, 1000,500,100,50 UPDATE MATRIX 1
REM ** Lift (8,3) to 25.7 *** REM *** Get the height every 10 units ***
SET MATRIX HEIGHT 1, 8, 3, 25.7 REM *** along 70.0 line of matrix ***
REM *** Sink (0,1) to -12.5 *** SYNC ON
REM *** End program *** REM *** Use camera to move about ***
WAIT KEY DO
END CONTROL CAMERA USING ARROWKEYS 0, 1, 1
LOOP
Second modification: REM *** End program ***
WAIT KEY
REM *** Set screen resolution *** END
SET DISPLAY MODE 1280, 1024, 16
Change
SHIFT MATRIX UP 1
to
and later to
Activity 45.15
No solution required.
Activity 45.16
No solution required.
Activity 45.17
The lower part of the cube can be seen through the
transparent matrix.
Activity 45.18
The lower part of the cube can no longer be seen.
Activity 45.19
The lighting effect changes subtly for different values.
Using negative values gives a much duller finish.
(-5,0,5) (5,0,5)
A Triangle Vertices
(0,0,0)
A triangle is the simplest polygon... ...and is defined by three vertices... ...with each vertex being defined by its
x,y,z coordinates.
The individual vetices of a 3D mesh or limb can be manipulated using a set of vertex
data statements that are included in DarkBASIC Pro. For example, we could start
with a cube and move one of its vertices to create a new shape (see FIG-46.2).
FIG-46.2
A Distorted Cube
The Statements
The LOCK VERTEXDATA FOR MESH Statement
Before we can begin to modify the vertex data of a mesh, we need to take a copy
of the coordinates of the mesh's vertices. To capture the vertex data we use the
LOCK VERTEXDATA FOR MESH statement which has the format shown in
FIG-46.3.
FIG-46.3
The LOCK LOCK VERTEXDATA FOR MESH meshno
VERTEXDATA FOR
MESH Statement
In the diagram:
From there we can save the new mesh's vertex data with the line
integer
For example, we could determine the number of vertices in our locked triangle (as
given in the example above) using the line:
The program in LISTING-46.1 brings together the example statements used earlier
to create a triangle, convert it to a mesh, store the triangle's data and display the
vertex count.
LISTING-46.1 SetUpScreen()
REM *** Create triangle ***
Finding the Number of MAKE OBJECT TRIANGLE 1,0,0,0,-5,0,5,5,0,5
Vertices in a Mesh REM *** Convert it to a mesh ***
MAKE MESH FROM OBJECT 1,1
DELETE OBJECT 1
REM *** Capture mesh vertex data ***
LOCK VERTEXDATA FOR MESH 1
REM *** Find out number of vertices in mesh ***
count = GET VERTEXDATA VERTEX COUNT()
REM *** Display number of vertices ***
DO
SET CURSOR 100,100
PRINT count
LOOP
REM *** End program ***
END
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON
AUTOCAM OFF
ENDFUNCTION
{
The GET X
VERTEXDATA
POSITION Statement
GET VERTEXDATA POSITION Y ( vertexno )
real
In the diagram:
The triangle mesh we created in the first program will have vertices numbered, 0,
1, and 2. To retrieve the coordinates of vertex 0 we would use the lines:
Activity 46.2
Add the following lines at appropriate positions within your last program and
hence display the coordinates of the second vertex (vertex 1) :
The next program (see LISTING-46.2) stores the coordinates of a triangles vertices
in a set of arrays - one array for all the x-ordinates, one for the y-ordinates, and one
for the z-ordinates. The coordinates of the vertices are then displayed. The program
includes a routine (Format$()) to create an aligned layout.
Displaying the REM *** Create root object for model ***
Coordinates of an MAKE OBJECT SPHERE 99,0
Object's Vertices
REM *** Create triangle ***
MAKE OBJECT TRIANGLE 1,0,0,0,-5,0,5,5,0,5
REM *** Create arrays to hold the coordinates of each vertex ***
DIM x#(count)
DIM y#(count)
DIM z#(count)
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,0,-20
POINT CAMERA 0,0,0
ENDFUNCTION
FUNCTION Format$(v#,intpart,decpl)
result$ = STR$(v#,decpl)
result$ = SPACE$(intpart+decpl-LEN(result$))+result$
ENDFUNCTION result$
Activity 46.3
In the diagram:
For example, we could change the coordinates of vertex 0 in our triangle to (5,5,5)
using the line:
However, this won't have any effect on the triangle on the screen. To do that we
must unlock the vertex values.
LISTING-46.3 SetUpScreen()
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(255,255,100)
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,0,-20
POINT CAMERA 0,0,0
ENDFUNCTION
Activity 46.4
Once the vertex data has been copied, the usual vertex data commands such as GET
VERTEXDATA INDEX COUNT, GET VERTEXDATA POSITION and SET
VERTEXDATA POSITION can be applied. When the UNLOCK VERTEXDATA
is executed the affected limb within the model will be modified.
In the next program (see LISTING-46.4) a triangle is added as limb 1 within a model
and then modified directly.
LISTING-46.4 SetUpScreen()
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(255,255,100)
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,0,-20
POINT CAMERA 0,0,0
ENDFUNCTION
Activity 46.5
we can then capture the triangle's vertex data with the line:
After that, it is just a matter of the usual vertex manipulation and then unlocking
the vertex data buffer.
Replace the main section of your last program with the lines
SetUpScreen()
Does the new program create the same effect as the previous version?
{
X
The GET
VERTEXDATA
NORMALS Statement GET VERTEXDATA NORMALS Y ( vertexno )
real
In the diagram:
For example, we could retrieve the triangle's vertex normal at vertex zero using the
lines:
x# = GET VERTEXDATA NORMALS X(0)
Activity 46.7
Modify your previous program to display the vertex normals for all three
points on the triangle using the following logic:
FOR vertexno = 0 TO 2
Get x,y, and z ordinates for vector normal
Position cursor at 100, 100 + vertexno * 20
Display x,y,z values
ENDFOR
In the diagram:
For example, we could change the normal at vertex 0 in our triangle to (0,0,0) using
the line:
As with any other change, the screen display will only change when the vertex data
is unlocked.
In the next program (see LISTING-46.5) we eliminate the normal at each vector by
setting their values to (0,0,0) to show how this affects the lighting.
LISTING-46.5 SetUpScreen()
Changing Vertex MAKE OBJECT TRIANGLE 1,0,0,0,-5,3,5,5,0,5
Normals WAIT KEY
REM ** FOR each vertex normal DO ***
FOR v = 0 TO 2
REM *** eliminate its normal ***
LOCK VERTEXDATA FOR LIMB 1,0
SET VERTEXDATA NORMALS v,0,0,0
UNLOCK VERTEXDATA
WAIT KEY
NEXT v continued on next page
Activity 46.8
V
( vertexno )
real
In the diagram:
The program in LISTING-46.6 displays the UV offsets for each vertex of a triangle.
LISTING-46.6 SetUpScreen()
MAKE OBJECT TRIANGLE 1,0,0,0,-5,3,5,5,0,5
Displaying Vertex UV LOAD IMAGE "Spot1.bmp",1
Settings TEXTURE OBJECT 1,1
LOCK VERTEXDATA FOR LIMB 1,0
DO
FOR vertexno = 0 TO 2
u# = GET VERTEXDATA U(vertexno)
v# = GET VERTEXDATA V(vertexno)
SET CURSOR 100, 100 + vertexno * 20
PRINT "UV value for vertex ",vertexno," U: ",STR$(u#,2),
" V : ",STR$(v#,2)
NEXT v
LOOP
WAIT KEY
END continued on next page
Activity 46.9
In the diagram:
creates the effect shown in FIG-46.13 on a triangle textured with the white dot on
a red background image.
FIG-46.13
The Effects of
Modifying Vertex UV
Settings
LISTING-46.7 SetUpScreen()
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(200,200,100)
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,10,-30
POINT CAMERA 0,0,0
ENDFUNCTION
Activity 46.10
For example, we could create a yellow diffuse at vertex 2 using the line:
FIG-46.15
GET VERTEXDATA DIFFUSE ( vertexno )
The GET
VERTEXDATA
DIFFUSE Statement
integer
Next up is the plane. At first we might be fooled into thinking that a plane can be
represented by 4 vertices, but, in fact, 6 are recorded in the vertex buffer.
Two triangles are required to create a plane and each triangle consists of 3 vertices
(see FIG-46.16).
FIG-46.16 1
3 0
All Objects are Created
from Triangles
4 2
5
A plane may appear to have only four ...but it is constructed using two triangles,
vertices... so there are 6 vertices (3 for each
triangle).
Activity 46.11
The program in LISTING-46.8 displays a plane in wireframe mode and lists the
coordinates of each of its vertices.
FUNCTION Format$(v#,intpart,decpl)
result$ = STR$(v#,decpl)
result$ = SPACE$(intpart+decpl-LEN(result$))+result$
ENDFUNCTION result$
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,10,-20
POINT CAMERA 0,0,0
ENDFUNCTION
Activity 46.12
Change the code so that a cube (10x10x10) is created. Does the number of
vertices used match the predicted figure?
We'll discover why a cube does not use the number of vertices we predicted later.
The vertex data specifies coordinates relative to the centre of the object, not as
absolute coordinates in 3D space. We can see this from the results of the Activity
below.
Activity 46.13
How does this affect the vertex coordinates displayed on the screen?
Another cause for confusion is that the vertex coordinates are measured using
reversed x and z axes. This means that negative x values are to the right and negative
LISTING-46.9 SetUpScreen()
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,0,-20
POINT CAMERA 0,0,0
ENDFUNCTION
Activity 46.14
Activity 46.15
Two other vertices have matching coordinates. What are those coordinates?
If we want to modify the position of a vertex, we may have to reposition all the
entries in the vertex data area which match that value. For example, in the plane
created in the program above.
If we start with a plane, and wish to ... the shape shown above... ... we need to move the bottom-left
distort it to... corner to the new position as shown.
Since the original shape is constructed ... adjusting a single entry in the vertex To achieve the correct shape, both
from two triangles... data modifies a single triangle. vertex data entries must be changed.
In a complex 3D shape a single point in space can be repeated over many vertices,
so it will be useful if we can find the position of every matching entry in the vertex
data.
DIM matchvertx(12)
count AS INTEGER
Since a function cannot return an array, we'll declare the array to be global. It's
probably easiest just to make count global too:
GLOBAL DIM matchvertx(12)
GLOBAL count AS INTEGER
Add 1 to count
ENDIF
ENDFOR
FIG-46.18 demonstrates the effect of searching for matches for vertex 2 of the
rectangle used earlier.
plane object 1
matchvertx
FindMatchingVertices(1,2)
count Object
Vertex
vertex 2
We start with the data areas in which Next, we choose the object and a Now we can call the function and
the results will be stored. vertex. search for vertices with matching coords.
FOR c = 1 TO count
matchvertx SET VERTEXDATA matchvertx(c),
-2.5,2.5,0
2 5 NEXT c
count
2
vertex 2
vertex 5
Vertex 5 has the same coords as So the function will make two entries ...and we can use this information to
vertex 2. in the data structure... move all relevant vertices and modify
the object's shape.
FUNCTION FindMatchingVertices(objno,vertexno)
LOCK VERTEXDATA FOR LIMB objno,0
x# = GET VERTEXDATA POSITION X(vertexno)
y# = GET VERTEXDATA POSITION Y(vertexno)
z# = GET VERTEXDATA POSITION Z(vertexno)
count = 0
FOR vno = 0 TO GET VERTEXDATA VERTEX COUNT()-1
IF x# = GET VERTEXDATA POSITION X(vno) AND
y# = GET VERTEXDATA POSITION Y(vno) AND
z# = GET VERTEXDATA POSITION Z(vno)
INC count
matchvertx(count) = vno
ENDIF
NEXT vno
UNLOCK VERTEXDATA
ENDFUNCTION
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,0,-20
POINT CAMERA 0,0,0
ENDFUNCTION
Activity 46.16
make the plane rotate about its y-axis while it is changing shape.
With a bit of imagination we can transform basic 3D shapes into more interesting
forms. For example, we can create a hemisphere by modifying all the vertices with
y values less than zero to have a y-ordinate of zero as shown in LISTING-46.11.
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,0,-20
POINT CAMERA 0,0,0
ENDFUNCTION
Activity 46.17
Activity 46.18
With the necessary mesh available, we now use the ADD MESH TO
VERTEXDATA statement to add the new vertex details to the buffer. The ADD
MESH TO VERTEXDATA statement has the format shown in FIG-46.19.
FIG-46.19
The ADD MESH TO ADD MESH TO VERTEXDATA meshno
VERTEXDATA
Statement
In the diagram:
Using this statement, we can add the details of the triangle object to the buffer with
the line:
The program in LISTING-46.12 adds a triangle's data to that of a cube in the vertex
data buffer, displaying the total number of vertices within the buffer both before
and after the triangle is added.
LISTING-46.12
SetUpScreen()
Adding Data to the
Vertex Data Buffer REM *** Make cube and place its data in buffer ***
MAKE OBJECT CUBE 1,10
LOCK VERTEXDATA FOR LIMB 1,0
FUNCTION DisplayMessage(mes$,x,y)
now = TIMER()
WHILE TIMER() - now < 3000
SET CURSOR x,y
PRINT mes$
ENDWHILE
ENDFUNCTION
Activity 46.19
How many vertices are in the buffer once the triangle's data has been added?
0,12,5 3,19,2 3,9,6 0,12,5 3,8,5 0,12,5 3,19,2 3,9,6 0,12,5 3,8,5
0 1 2 3 4 0 1 2 3 4
0,1,2 3,4,5
The coordinates of
each vertex in the
3D object The first entry states
that vertices 0,1, and 2
make up the first polygon
in the 3D object
If we create a shape and then lock its ...we have access to a data structure ...but also details on how these vertices
vertex data buffer... that not only contains a list of the are joined together.
vertices within that shape...
As we can see, a second array within the vertex buffer contains details of how the
vertices are linked to form the triangular polygons that make up the structure. The
number of entries in this second array should match the number of triangles required
to construct the 3D object represented.
For example, a triangle object only needs 1 entry, a plane 2, and a cube 12 (two for
each side of the cube). To differentiate between the two sets of data, we'll call the
first area the vertex array and the second one the polygon array. By using this second
array detailing the points used to construct each polygon, the need to duplicate
vertex coordinates in the first array disappears. The reasoning behind this is shown
in FIG-46.21.
1 Duplication
3 0 vertex array
vertex array
5,5,0 -5,5,0 5,-5,0 -5,-5,0
5,5,0 -5,5,0 5,-5,0 -5,5,0 -5,-5,0 5,-5,0 0 1 2 3 4 5
0 1 2 3 4 5
No duplicated
0 1 2 1 2 3 vertices
Duplication
polygon array
4 2
5
When a plane is created its coordinates ...with details of all six points recorded - ...but by detailing which points link to
can be saved in the vertex buffer... even though some of these are make a polygon, duplication of vertices
duplicates... can be eliminated.
In fact, both methods are used. For simpler objects, such as triangles and planes,
only the vertex array is created, but for more complex shapes, such as cubes,
duplicate vertices are eliminated from the vertex array and details of which points
make up each polygon are recorded in the polygon array.
integer
The statement actually returns the number of values in the polygon array, and, since
there are three values for each polygon, we'll get three times the expected amount
The program in LISTING-46.13 creates a cube and displays the number of entries
in the vertex array and the polygons used to construct the object. This last value is
arrived at by dividing the value returned by GET VERTEXDATA INDEX COUNT
by three.
LISTING-46.13 SetUpScreen()
REM *** Make cube and place its data in buffer ***
Displaying a Cube's MAKE OBJECT CUBE 1,10
Vertex Data LOCK VERTEXDATA FOR LIMB 1,0,2
SYNC
REM *** Display number of vertices and polygons ***
noofvertices = GET VERTEXDATA VERTEX COUNT()
noofpolygons = GET VERTEXDATA INDEX COUNT()/3
PRINT "Vertices : ", noofvertices
PRINT "Polygons : ", noofpolygons
SYNC
REM *** End program ***
WAIT KEY
END
Activity 46.20
a) a triangle
b) a plane
c) a cone
Notice that the triangle and plane return a value of zero for the polygon count. This
is because there are only three vertices in the triangle, so retaining information about
how these should be joined is unnecessary; the plane, on the other hand, retains
duplicated vertices and polygons are created by using the vertex array entries in
groups of three (that is, entries 0, 1 and 2 form the first polygon; 3,4 and 5 the
second polygon; etc.)
FIG-46.23
GET INDEXDATA ( sub )
The GET INDEXDATA
Statement
integer
In the diagram:
We could use this statement to discover which entries in the vertex array are used
to construct a specific polygon. For example, assuming we have stored data about
a cube within the vertex buffer, we could discover which cells of the vertex array
are used in the construction of the first polygon on the first face of the cube using
the lines
v1 = GETINDEXDATA(0)
v2 = GETINDEXDATA(1)
v3 = GETINDEXDATA(2)
and with this information we could go on to discover the coordinates of each of the
three points:
Activity 46.21
Write a program which creates a 10 unit cube and displays which elements of
the vertex array are used to construct its first two polygons.
The program in LISTING-46.14 takes the program in Activity 46.21 a stage further
and displays the coordinates used in constructing the second polygon of the cube.
LISTING-46.14 TYPE CoordsType
x#
Displaying Vertex y#
Coordinates z#
ENDTYPE
SetUpScreen()
REM *** Make cube and place its data in buffer ***
MAKE OBJECT CUBE 1,10
LOCK VERTEXDATA FOR LIMB 1,0,2
REM *** Collect coordinates ***
FOR c = 1 TO 3
v = GET INDEXDATA(c+2)
polycoords(c).x# = GET VERTEXDATA POSITION X(v)
polycoords(c).y# = GET VERTEXDATA POSITION Y(v)
polycoords(c).z# = GET VERTEXDATA POSITION Z(v)
NEXT c
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 0,0,-20
POINT CAMERA 0,0,0
SET CAMERA VIEW 0,0,800,600
ENDFUNCTION
Activity 46.22
In the diagram:
For example, assuming a cube's details are stored in the vertex buffer, we could
change the vertices used to construct the first polygon in the cube to those stored in
cells 7, 8, and 9 of the vertex array using the lines:
Modifying a Cube's REM *** Make cube and place its data in buffer ***
Structure MAKE OBJECT CUBE 1,10
LOCK VERTEXDATA FOR LIMB 1,0,2
REM *** Modify details of how the first polygon is constructed ***
WAIT KEY
SET INDEXDATA 0,3
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 0,0,-20
POINT CAMERA 0,0,0
ENDFUNCTION
Activity 46.23
To delete part of the vertex data buffer we use the DELETE MESH FROM
VERTEXDATA statement which has the format shown in FIG-46.26.
FIG-46.26
The DELETE MESH DELETE MESH FROM VERTEXDATA vertindx1 , vertindx2
FROM VERTEXDATA
Statement
, polyindx1 , polyindx2
In the diagram:
For example, if we place the vertex data for a cube into the vertex buffer, 24 cells
in the vertex array will be occupied (cells 0 to 23) and 36 cells in the polygon array
(cells 0 to 35). Adding a second cube to the vertex buffer would result in an
additional 24 entries in the vertex array (cells 24 to 47) and 36 extra entries in the
polygon array (cells 36 to 71). To delete this second cube from the vertex buffer we
would use the statement:
l Use LOCK VERTEXDATA FOR MESH to capture into the buffer area the
vertices that make up a mesh.
l Use LOCK VERTEXDATA FOR LIMB to capture the vertex data of a limb or
object.
l Use GET VERTEXDATA {UV} to access the U and V offsets at a given vertex
for a textured object.
l Use SET VERTEXDATA DIFFUSE to modify the diffuse setting for a vertex.
l Use GET VERTEXDATA DIFFUSE to access the current diffuse setting for a
specific vertex.
l If a vertex's coordinates are modified, generally, all other vertices with identical
coordinates should also be modified.
l Vertex coordinates are relative to the object's centre and not absolute world
coordinates.
l Use ADD MESH TO VERTEXDATA to add the details of a mesh to the data
already stored in the vertex buffer.
l The vertex data buffer contains an array (the vertex array) which holds the
coordinates of every vertex used to make up the 3D object(s) held in the buffer.
l For more complex shapes, each vertex's coordinates are stored only once and a
second array (the polygon array) holds links to those vertices used to construct
each polygon.
l Use SET INDEXDATA to modify which vertex an entry in the polygon array
references.
l Use DELETE MESH FROM VERTEXDATA to remove a mesh's data from the
vertex data buffer.
FUNCTION SetUpScreen()
No solution required.
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON Activity 46.15
AUTOCAM OFF
POSITION CAMERA 0,10,-20 Vertices 1 and 3 have the coordinates (5,5,0).
POINT CAMERA 0,0,0
ENDFUNCTION Vertices 2 and 4 have the coordinates (-5,-5,0).
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
Activity 46.12 COLOR BACKDROP 0
BACKDROP ON
To make a cube, change the line
AUTOCAM OFF
POSITION CAMERA 0,0,-20
MAKE OBJECT PLAIN 1,10,10 POINT CAMERA 0,0,0
ENDFUNCTION
to
Version 2 ( rotating animated transform )
MAKE OBJECT CUBE 1,10
GLOBAL DIM matchvertx(12)
The cube used 24 vetices rather than the predicted 36. GLOBAL count AS INTEGER
FUNCTION FindMatchingVertices(objno,vertexno)
Ä
x#,y#,z#
ENDIF
LOCK VERTEXDATA FOR LIMB objno,0 NEXT vertexno
x# = GET VERTEXDATA POSITION X(vertexno) UNLOCK VERTEXDATA
y# = GET VERTEXDATA POSITION Y(vertexno) REM *** Rotate pyramid ***
z# = GET VERTEXDATA POSITION Z(vertexno) DO
count = 0 TURN OBJECT LEFT 1,1
FOR vno = 0 TO
ÄGET VERTEXDATA VERTEX COUNT()-1
WAIT 10
LOOP
IF x# = GET VERTEXDATA POSITION X(vno)
Ä
AND y#=GET VERTEXDATA POSITION Y(vno)
Ä
REM *** End program ***
AND z#=GET VERTEXDATA POSITION Z(vno) END
INC count
matchvertx(count) = vno FUNCTION SetUpScreen()
ENDIF SET DISPLAY MODE 1280,1024,32
NEXT vno COLOR BACKDROP RGB(200,200,100)
UNLOCK VERTEXDATA BACKDROP ON
ENDFUNCTION AUTOCAM OFF
POSITION CAMERA 0,0,-20
FUNCTION SetUpScreen() POINT CAMERA 0,0,0
SET DISPLAY MODE 1280,1024,32 ENDFUNCTION
COLOR BACKDROP 0
BACKDROP ON
AUTOCAM OFF Activity 46.19
POSITION CAMERA 0,0,-20
POINT CAMERA 0,0,0 The cube creates 24 entries in the buffer while the
ENDFUNCTION triangle adds another 3 giving a total of 27.
The plane:
The cone:
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,0,-20
POINT CAMERA 0,0,0
SYNC ON
ENDFUNCTION
Activity 46.22
No solution required.
Activity 46.23
No solution required.
lives = 3
the computer reserves a few bytes of memory for that variable (see FIG-47.1) and
it's in those bytes that the variable's contents are stored.
A bit is the smallest unit of storage. Computer memory is a sequence of Every variable in a program is allocated
A byte is a group of 8 bits. bytes, each given its own address. one or more bytes somewhere in
memory.
Most of the time, finding out which locations have been allocated to a variable is
of no interest to us since we can access its contents by using the variable's name.
In fact it's not just variables that are allocated memory space; every item of data
used by a program, from simple integers to images, sounds and 3D objects will all
be allocated memory within RAM when a program is executing.
Pointers
While most variables store the traditional numeric or string values, there is a special
variable type capable of storing the address at which an item of normal data is held.
Such a variable is known as a pointer (see FIG-47.2).
ptr AS DWORD
ptr = 2009
and treat that value as a memory address (after all, there's bound to be a memory
location which has that address) but, in practice, this won't work since the operating
system will not allow a program to access parts of the memory that have not been
allocated to that program.
Instead, we need to ask the program to reserve a new area of memory and then place
the address of this new area in the pointer.
In the diagram:
For example we could allocate a single byte to memory block 1 using the line:
integer
Using this statement, we can assign the memory block's address to our pointer:
Using a Pointer
Now that the pointer contains the address of the memory block, we can use the
pointer to access the reserved area. To do this we dereference the pointer. This
simply means that we use the contents of the pointer to access the memory location
specified (see FIG-47.5).
FIG-47.5 Dereferencing
a pointer gives
Dereferencing a Pointer access to the memory
location specified
2009 A
ptr
If this seems a strange idea, imagine looking at a treasure map; the map does not
contain the treasure - but it tells you where to find it. A pointer variable doesn't
contain the data you're searching for, but it tells you where to find it.
In DarkBASIC Pro we put an asterisk (*) in front of the pointer variable in order to
dereference it. For example, the statement
*ptr = 65
will store the value 65 at the memory location referenced by ptr, and
PRINT *ptr
It is important that you understand that the term ptr refers to the contents of the
variable ptr and that the term *ptr refers to the contents of the address specified by
ptr (see FIG-47.6).
The problem now is that we need some method of storing data in the different parts
of this area (see FIG-47.7).
x y z x y z
12 bytes
MAKE MEMBLOCK 1,12 will reserve We'd like to divide this into 3 groups ...but how are the extra bytes to be
12 bytes. each of 4 bytes to store coordinates... accessed?
{
BYTE
WORD
WRITE MEMBLOCK memno , position , value
DWORD
FLOAT
In the diagram:
For example, assuming the spaceship's coordinates are (2.0,5.7,3.9), we could write
these values to memory block 1 using the statements:
{
BYTE
The MEMBLOCK
Statement
WORD
MEMBLOCK ( memno , position )
DWORD
FLOAT
The type of value returned by this statement depends on the type of value being
read. The BYTE and WORD options both return an integer value, DWORD returns
a DWORD type, and FLOAT returns a real.
integer
In the diagram:
In the diagram:
FIG-47.13
MEMBLOCK EXIST ( memno )
The MEMBLOCK
EXIST Statement
integer
Copying is done using the COPY MEMBLOCK statement which has the format
shown in FIG-47.15.
In the diagram:
For example, if we have previously created two memory blocks using the statements
MAKE MEMBLOCK 1, 20
MAKE MEMBLOCK 2, 50
and then placed data in memory block 1, the third 10 bytes of data in memory block
1 can be copied to the fourth group of 10 bytes in memory block 2 using the
statement:
Now we need to discover how many characters are in the string so we can create a
memory block of appropriate size:
characters = LEN(name$)
MAKE MEMBLOCK 1,characters
Since we can only transfer numbers to the memory block, we'll convert each
character in the string to a numeric value and write it to the memory block:
FOR c = 1 TO characters
value = ASC(MID$(name$,c))
WRITE MEMBLOCK BYTE 1,c-1,value
NEXT c
To retrieve the information, we need to create an empty string, read each value from
the block, convert it back to a character, and add the character to the end of our
string we have just created:
result$=""
FOR c = 0 TO GET MEMBLOCK SIZE(1)-1
ch$ = CHR$(MEMBLOCK BYTE(1,c)
result$ = result$ + ch$
NEXT c
In the next program we'll be a bit more ambitious. This time we'll use a memory
block to store the top five scores in a game. Each entry will contain the player's
name and his score.
But what about names that are less than 25 characters in length? We just pad them
out with spaces to make them the required length. Names greater than 25 just won't
be accepted.
FOR 5 times DO
Get name
Get score
ENDFOR
FUNCTION GetName$()
INPUT "Enter name ",name$
WHILE LEN(name$) > 25
INPUT "Invalid name. Please re-enter ",name$
ENDWHILE
REM *** Pad name to 25 characters ***
name$=name$+SPACE$(25 - LEN(name$))
ENDFUNCTION name$
FUNCTION GetScore()
INPUT "Enter score ",score
WHILE score < 0
INPUT "Invalid score. Please re-enter ",score
ENDWHILE
ENDFUNCTION score
FUNCTION AddDataToList(name$,score,post)
byteposition = (post-1)*29
REM *** Store name
FOR c = 1 TO 25
continued on next page
FUNCTION DisplayContentsOfMemBlock()
FOR c = 1 TO 5
DisplayEntry(c)
NEXT c
ENDFUNCTION
FUNCTION DisplayEntry(post)
IF post < 1 OR post > 5
EXITFUNCTION
ENDIF
byteposition = (post-1)*29
REM *** Read name back ***
name$=""
FOR c = byteposition TO byteposition + 24
ch$ = CHR$(MEMBLOCK BYTE(1,c))
name$ = name$ + ch$
NEXT c
score = MEMBLOCK DWORD(1,byteposition + 25)
PRINT name$," ",score
ENDFUNCTION
Activity 47.4
The statement writes not only the contents of the memory block, but also its size.
The file will be created automatically in the current directory by the OPEN TO
WRITE statement. The whole memory block can be written to the file with the
single line:
WRITE MEMBLOCK 1,1
The lines have been placed in a function which also checks that the file does not
exist before it is opened:
FUNCTION SaveListToFile()
IF FILE EXIST("toplist.dat")
DELETE FILE "toplist.dat"
ENDIF
OPEN TO WRITE 1, "toplist.dat"
WRITE MEMBLOCK 1,1
CLOSE FILE 1
ENDFUNCTION
Activity 47.5
Add the function above to your last program and change the main section so
that the function is called before the program terminates.
A file produced in this way is not limited to storing data from a memory block;
additional information can be written to the file either before or after the data from
the memory block. For example, it might prove useful to write to the file details
such as the number of entries in the memory block, the size of an entry, the number
of fields in one entry and a description of the fields within an entry. For our top-five
list, that would involve saving the following information:
Activity 47.6
We still need to open and close a file, but using this statement can be more
convenient if nothing but the contents of the memory block are to be written to the
file. Below is a second function for writing the top-five list to a file:
FUNCTION SaveListToFile2()
IF FILE EXIST("toplist2.mem")
DELETE FILE "toplist2.mem"
ENDIF
OPEN TO WRITE 1, "toplist2.mem"
MAKE FILE FROM MEMBLOCK 1,1
CLOSE FILE 1
ENDFUNCTION
Activity 47.7
Add the new function above to your last program and modify the main section
so that only this new function is used to save the memory block to a file.
In the diagram:
If we create flexible code, we can read back almost any memory block by
interpreting the information we find in the header.
We start by declaring global variables which will hold the header data:
FUNCTION ReadListFromFile()
REM *** Open file for reading ***
OPEN TO READ 1, "toplist.dat"
REM *** Read header data ***
READ LONG 1,records
READ LONG 1,recsize
READ LONG 1,fields
FOR c = 1 TO fields
READ STRING 1,field$
fieldformat$(c) = LEFT$(field$,1)
fieldsize(c) = VAL(RIGHT$(field$, LEN(field$)-1))
NEXT c
REM *** Read top 5 details ***
READ MEMBLOCK 1,1
REM *** Close the file ***
CLOSE FILE 1
ENDFUNCTION
and an individual record within the memory block can be displayed using the
following function:
FUNCTION DisplayEntry(post)
IF post < 1 OR post > records
EXITFUNCTION
ENDIF
byteposition = (post-1)*recsize
REM *** Calculate position of record within memblock ***
offset = (post-1)*recsize
REM *** Display each field in the record ***
FOR c = 1 TO fields
SELECT fieldformat$(c)
CASE "S"
text$=""
FOR k = byteposition TO byteposition +
Ä
fieldsize(c)-1
ch$ = CHR$(MEMBLOCK BYTE(1,offset))
text$ = text$ + ch$
INC offset
NEXT k
PRINT text$," ";
ENDCASE
CASE "D"
value = MEMBLOCK DWORD(1,offset)
offset = offset + 4
PRINT value," ";
ENDCASE
ENDSELECT
Activity 47.8
FIG-47.19
The MAKE
MAKE MEMBLOCK FROM FILE fileno , memno
MEMBLOCK FROM
FILE Statement
In the diagram:
For example, we could read back the contents of toplist2.mem using the code:
Activity 47.9
We can achieve the first step in this process - moving existing entries - using the
COPY MEMBLOCK statement.
For example, to add Mary Gibson to our list, we could move entries 3 and 4 into
positions 4 and 5 using the line:
If a new high score is achieved... ...we need to move existing entries... ...to make space for the new entry.
Activity 47.10
Summary
l A pointer is a variable designed to contain an address.
l Use GET MEMBLOCK PTR to return the start address of a memory block.
l The file specified in a WRITE MEMBLOCK statement may contain data other
than that from the memory block.
l Use MAKE FILE FROM MEMBLOCK to create a file containing only the
contents of a memory block.
l Use MAKE MEMBLOCK FROM FILE to read data from a file containing only
data copied from a memory block into a new memory block.
In the diagram:
To copy the contents of the screen to memory block 1 we would use the statement:
MAKE MEMBLOCK FROM BITMAP 1,0
If we are to manipulate the contents of the memory block, we need to know the
structure of the data held. A memory block containing a bitmap has the format
shown in FIG-47.22.
As you can see, the first 12 bytes of the memory block contain details of the bitmap's
width, height and bits per pixel. For most bitmap areas this will correspond to the
details of the actual image that has been loaded into that area, but for bitmap area
0, which is the screen, the width, height and bits per pixel are determined by the
values used in the SET DISPLAY MODE statement.
Activity 47.11
Set the screen to 800 by 600 with 16 bits per pixel and check that the details
returned match this figure.
Modify the program to display the number of bytes required by a single line of
pixels on the screen.
Activity 47.12
We can make use of the data in the memory block to actually change what appears
on the screen by first changing values within the pixel data area of the memory
For example, if we assume a screen resolution of 800 by 600 and 16 bits per pixel,
we could make the data representing the top-left pixel on the screen white by
beginning with the line:
So, if we've changed the contents of memory block 1, we can write the updated
version back to the screen using the line:
The program in LISTING-47.6 starts by loading the black screen image into a
memory block and changes the first pixel to white before rewriting the memory
block's data to the screen.
LISTING-47.6 REM *** Set screen resolution ***
SET DISPLAY MODE 1280,1024,32
Manipulating a Bitmap's REM *** Copy screen to memory block ***
Data MAKE MEMBLOCK FROM BITMAP 1,0
PRINT "Screen captured"
WAIT KEY
REM *** Change first pixel in block to white ***
WRITE MEMBLOCK DWORD 1,12,RGB(255,255,255)
PRINT "Memblock changed"
WAIT KEY
REM *** Rewrite block to screen ***
MAKE BITMAP FROM MEMBLOCK 0,1
PRINT "Screen updated"
REM *** End program ***
WAIT KEY
END
Activity 47.13
Make sure that your
monitor is adjusted to Type in and test the program in LISTING-47.6 (mem06.dbpro).
show all pixels that make
up the screen output. Modify the program so that it is the 10th pixel on the first row that is made
white.
Pixels are addressed using a column and row system, starting with column zero,
row zero. So, pixel (0,0) has its data stored at position 12 in the memory block. If
we are using a 32 bits / pixel setup, then pixel (1,0) starts at position 16, pixel (2,0)
at position 20, etc. More generally, we can write that:
If the image is 1280 pixels wide (as the screen is in the earlier examples) then the
last pixel on the first line - pixel (1279,0) will start at position 1279*4 + 12 = 5128
and therefore, the first pixel in the next row (0,1) must start at position 5132.
Activity 47.14
Modify your last program to make the last pixel of the first row and the first
pixel of the second row white.
If each pixel requires 4 bytes of storage, and there are 1280 pixels in a row, then 1
row requires 5120 pixels. Now we can extend our formula to calculate the position
of any pixel's data within the memory block to be:
Activity 47.15
Modify your last program to change the bottom right pixel on the screen -
pixel (1279,1023) - to white
Our formula is still too specific - it assumes a resolution of 1280 by 1024 and 32
bits per pixel. A more general formula would be:
The program in LISTING-47.7 loads an image, bbrush.jpg, onto the screen and then
creates a negative version of the image by inverting the value of every pixel in the
image.
Activity 47.16
Activity 47.17
Activity 47.18
Modify the program so that a mouse button must be pressed before a pixel
turns yellow.
The memory block created by this statement has exactly the same format as that
created with a bitmap, with the first 12 bytes giving the width, height and bits per
pixel, before the image's own data is reached.
We could load an image into a memory block with statements such as:
The image ID specified in this statement should not be in use when the statement
is executed.
Activity 47.19
The header information included at the start of a sound object is much more complex
than that of a bitmap or image. Details such as the sound format being used, number
of channels and sample rate are included. The actual structure or this header data is
an agreed international standard and is shown in FIG-47.27.
Sound
FIG-47.27 Memory Block Structure
Sound Data
format code Many sound file formats use this same core
data structure, but with different data compression
algorithms. The actual type of compression used
is identified here using a coded value registered
with Microsoft.
samples per second Identifies how often the original sound has been
sampled per second during the digitising session.
average bytes per second Specifies the average data transfer rate in bytes.
Normally, this will be
samples per second x block alignment
block alignment This value gives the size of each data block
sample. It should be equal to
channels x bits per sample
The program in LISTING-47.10 loads a sound file and displays the information
held in its header area.
Modify the program so that within the memory block the sample rate is set to
44100 and the bytes per second to 88200. Delete sound 1 and recreate a sound
1 object from memory block 1. Play the sound. How have the changes altered
the sound playback?
Mesh data is held in a format known as Flexible Vertex Flags or FVF which contains
details of a vertex's coordinates, its normal, its diffuse value and UV texture offset.
The program in LISTING-47.11 creates a mesh from a cube, stores the data in a
memory block, and displays the block's header details.
Activity 47.21
Most DarkBasic Pro models seem to use format FVF 274 which holds, for each
vertex, the information shown in FIG-47.31.
Vertex Entry Structure
FIG-47.31
Length
The Detailed Header (bytes)
Structure for a Mesh
4 x-ord FLOAT
4 y-ord FLOAT
4 z-ord FLOAT
4 x-normal FLOAT
4 y-normal FLOAT
4 z-normal FLOAT
4 U offset FLOAT
4 V offset FLOAT
The program in LISTING-47.12 displays the details of each vertex of the cube we
created previously.
LISTING-47.12 #CONSTANT cubeobj 1
#CONSTANT meshobj 1
Accessing Vertex
#CONSTANT memobj 1
Details from a Memory
Block
REM *** Manual screen updating ***
SYNC ON
Another common format for 3D objects is FVF format 338. This structure is similar
to 274 but includes a DWORD diffuse value in each vertex data block between the
normal's z-ordinate and the U offset value.
Each group of three vertices in the object are used to create a triangular polygon;
the collection of polygons form the object.
SetUpScreen()
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 0,0,-30
ENDFUNCTION
Activity 47.23
Modify the program so that, rather than change the coordinates of specific
vertices, the program changes all vertex normal coordinates to (1,1,1).
Activity 47.24
Modify your last program so that the cube's mesh is updated rather than
deleted and recreated.
l A bitmap's data starts with header information detailing width, height and colour
depth of the bitmap.
l A sound object's data starts with header information such as compression format,
channels, and sample rate data.
l A mesh's data starts with header information detailing the FVF format and the
number of vertices in the mesh.
l Following the mesh header are details about each vertex within the mesh.
l Vertex data varies with the FVF format being used, but includes the vertex's
spatial coordinates, normal coordinates and UV offset information.
The code is much simpler than that in Activity 47.8, but is should be changed to:
designed to handle a specific memblock structure only.
SET DISPLAY MODE 800,600,16
Activity 47.21
No solution required.
Activity 47.22
No solution required.
Activity 47.23
#CONSTANT cubeobj 1
#CONSTANT rootobj 2
#CONSTANT meshobj 1
#CONSTANT memobj 1
SetUpScreen()
REM *** Create memory block from cube ***
MAKE OBJECT CUBE cubeobj,10
MAKE MESH FROM OBJECT meshobj,cubeobj
DELETE OBJECT cubeobj
MAKE MEMBLOCK FROM MESH memobj,meshobj
DELETE MESH meshobj
REM *** Get details of object ***
entrysize = MEMBLOCK DWORD(1,4)
noofvertices = MEMBLOCK DWORD(1,8)
REM *** Get coords of each vertex ***
FOR c = 0 TO noofvertices-1
REM *** Calc start of normal data ***
post = 12 + c*entrysize + 12
REM *** Modify all normals to (1,1,1) ***
WRITE MEMBLOCK FLOAT 1,post,1
WRITE MEMBLOCK FLOAT 1,post+4,1
WRITE MEMBLOCK FLOAT 1,post+8,1
NEXT c
REM *** Make mesh from memory block ***
MAKE MESH FROM MEMBLOCK meshobj, memobj
REM *** Add mesh as limb to make it visible
***
MAKE OBJECT SPHERE rootobj,0
ADD LIMB rootobj,1,meshobj
REM *** Rotate object created ***
DO
PITCH OBJECT UP 2,1
WAIT 10
LOOP
REM *** End program ***
END
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 0,0,-30
ENDFUNCTION
Activity 47.24
The line
Although this lack of real-world laws can give us great freedom in what happens in
our 3D world, it also makes simulations of real life difficult.
So to help things along, DarkBASIC Pro has some basic commands built-in which
give us a hint at what is possible when the laws of physics are applied to our objects.
The ODE statements in DarkBASIC Pro are undocumented and although they are
still present in DarkBASIC Pro version 1.062, they need to be loaded separately.
The details given in this chapter are based on the results obtained from various
DarkBASIC Pro (v1.059) test programs and from other ODE documentation. If
you're impressed by these features, get hold of The Game Creator's DarkPhysics
add-on for really professional results.
While our own 3D objects are visible, ODE objects are always invisible. But when
an ODE object reacts to physical laws, it causes the visible object to which it is
attached to react in the same way.
The ODE box would be exactly the same size as the cube which it surrounds.
Should we want to make the ODE box twice the size of the cube, then we could use
the line
This statement must appear in your program before any other ODE related
statement.
Now we're ready to make a start on using ODE. In LISTING-48.1 a cube falls from
above, under the influence of gravity.
ScreenSetUp()
LISTING-48.1 REM *** Make cube ***
MAKE OBJECT CUBE 1,10
Getting Started with ODE POSITION OBJECT 1,0,25,0
WAIT KEY
REM *** Start Physics engine ***
ODE START
REM *** Link ODE box to cube ***
ODE CREATE DYNAMIC BOX 1
REM *** Update screen ***
DO
ODE UPDATE
LOOP
REM *** End program ***
ODE END
END
FUNCTION ScreenSetUp()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,10,-50
POINT CAMERA 0,0,0
ENDFUNCTION
Activity 48.1
In the diagram:
The default gravitational force is down the y-axis only and is equivalent to the
statement:
Activity 48.2
In your last program, add the following line immediately after ODE START
Activity 48.3
Add the above lines near the start of your last program.
Add a WAIT KEY statement before starting the ODE engine. Does the cube
stop when it hits the ground?
The cube goes straight through the ground plane. Of course, this is because the plane
object does not have an ODE box.
Activity 48.4
at the appropriate point in your program. Does the cube stop when it hits the
ground?
And so another problem becomes apparent! Putting a dynamic box around any
object means it is affected by gravity just like the cube.
For example, we could link a static collision box to object 2 using the line:
Activity 48.5
Modify the last program to link a static box to the plane object.
Modify the dynamic box around the cube so that it is has width, height and
depth of 20 units.
Activity 48.6
Change the ODE volume around the 3D cube to a sphere. How does this affect
the collision?
The program in LISTING-48.2 creates a cube, a sphere and a cylinder, then links
each object to the appropriate ODE dynamic volume before letting all three drop to
the ground.
LISTING-48.2 ScreenSetUp()
FUNCTION ScreenSetUp()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,10,-50
POINT CAMERA 0,0,0
ENDFUNCTION
In LISTING-48.3 the Hivebrain model uses an ODE mesh to detect its collision
with the ground.
LISTING-48.3 ScreenSetUp()
REM *** Load model ***
Using a Triangle Mesh LOAD OBJECT "H-Alien Hivebrain-Move.x",1
SCALE OBJECT 1, 500,500,500
POSITION OBJECT 1, 0,30,0
FUNCTION ScreenSetUp()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,10,-50
POINT CAMERA 0,0,0
ENDFUNCTION
Change the ODE volume used on the model to a box. Is this more accurate?
In the diagram:
The effect of this statement is to speed up or slow down movement (since the time
scale is changed) as well as affecting the accuracy of the movements.
Activity 48.9
LISTING-48.4 ScreenSetUp()
FUNCTION ScreenSetUp()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,10,-50
POINT CAMERA 0,0,0
ENDFUNCTION
Activity 48.10
As we see, the cube stops when it hits the cone. It doesn't tumble, because the cone
is using a box collision volume. A more accurate result would be obtained if a static
mesh was used on the cone. This is done using the ODE CREATE STATIC
TRIANGLE MESH statement which has the format shown in FIG-48.12.
FIG-48.12
The ODE CREATE
ODE CREATE STATIC TRIANGLE MESH objno
STATIC TRIANGLE
MESH Statement
In the diagram:
Activity 48.11
In your last program, change the ODE volume for object 2 to use a static mesh.
In performing this calculation some rounding errors will occur. How these errors
In the diagram:
For example, we could switch off corrective action using the line:
In fact, this statement can have a major effect on how objects react to collisions. In
the next Activity we'll see just what effects it can have on the cube as it falls onto
the cone.
Activity 48.12
Try running the program again with first a value of 1.0 and then 0.5 for the
ERP.
In the diagram:
FDIR1 stands for You should have seen how the cube slides along the surface after it lands. This is
Friction DIRection 1 due to a lack of friction - as if the block was made of melting ice. We can set the
(there's a second friction friction of a dynamic object using the ODE SET CONTACT FDIR1 statement
direction).
which has the format shown in FIG-48.15.
FIG-48.15
The ODE SET ODE SET CONTACT FDIR1 objno , friction
CONTACT FDIR1
Statement
In the diagram:
To add a friction coefficient to our falling cube we could use the line:
ODE SET CONTACT FDIR1 1,50
Activity 48.14
Add the above statement to your last program. Insert the line immediately
after the ODE volume is assigned to the cube.
In the diagram:
For example, we could make object 1 move horizontally using the line:
The program in LISTING-48.5 moves a cube from left to right above the ground
plane. Notice that gravitational forces are set to zero.
LISTING-48.5 ScreenSetUp()
REM *** Make objects ***
Giving an Object a MAKE OBJECT CUBE 1,7
Velocity POSITION OBJECT 1, -20,20,0
MAKE OBJECT PLAIN 4,100,100
LOAD IMAGE "grid8by8.bmp",1
TEXTURE OBJECT 4,1
XROTATE OBJECT 4,-90
REM *** Wait to start ODE ***
WAIT KEY
ODE START
REM *** Build ODE volumes ***
ODE CREATE DYNAMIC BOX 1
ODE CREATE STATIC BOX 4
REM *** No gravity ***
ODE SET WORLD GRAVITY 0,0,0
REM *** Add a velocity to the cube ***
ODE SET LINEAR VELOCITY 1,20,0,0
REM *** Continually update physics ***
DO
ODE UPDATE
LOOP
REM *** End program ***
ODE END
END
FUNCTION ScreenSetUp()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,10,-50
POINT CAMERA 0,0,0
ENDFUNCTION
Activity 48.15
Activity 48.16
In the next program (see LISTING-48.6), two cubes travel towards each other and
collide.
LISTING-48.6 ScreenSetUp()
Colliding Bodies REM *** Make cubes ***
MAKE OBJECT CUBE 1,7
POSITION OBJECT 1, -20,20,0
MAKE OBJECT CUBE 2,7
POSITION OBJECT 2,20,20,0
FUNCTION ScreenSetUp()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,10,-70
POINT CAMERA 0,0,0
ENDFUNCTION
Activity 48.17
Activity 48.18
In your last program, what effect is achieved for each of the following settings
in the ODE SET LINEAR VELOCITY statement for object 1:
xcomp ycomp zcomp
4 0 0
4 -1 0
In the diagram:
To keep the spin to a reasonably slow rate, xcomp, ycomp, and zcomp values should
be less than 1.
For example, to create a slow spin of object 1 with rotation about the x-axis being
half the speed of rotation about the y-axis, we could use the line:
Activity 48.19
Modify your last program by including the statement given above immediately
after the linear velocity of object 1 has been set.
Move the ODE SET ANGULAR VELOCITY statement placing it within the
program's DO..LOOP structure.
The ODE SET BODY ROTATION statement has the format shown in FIG-48.18.
FIG-48.18
The ODE SET BODY ROTATION Statement
In the diagram:
For example, we could rotate object 1 45o about its y-axis using the statement:
ODE SET BODY ROTATION 1,0,45,0
Activity 48.20
Notice that the cube is set to a specific angle and is then fixed. Since this
statement creates a single movement of the object, it need not be within the
DO..LOOP structure.
Move the ODE SET BODY ROTATION statement to a position before the
loop structure and check that the overall effect remains the same.
Activity 48.21
Modify your last program so that the mass of object 1 is set to 8 and that of
object 2 to 100.
Set gravity to -0.1. How are the cubes affected this time?
FIG-48.20
Tumbling Blocks
LISTING-48.7 ScreenSetUp()
Tumbling Blocks REM *** Load image used to texture objects ***
LOAD IMAGE "grid8by8.bmp",1
FUNCTION ScreenSetUp()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP 0
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 50,25,-150
POINT CAMERA 0,0,0
ENDFUNCTION
Activity 48.22
Modify the program so that the camera can be moved about within the scene.
(HINT: include the camera.dba routines we created in Chapter 33.)
In the diagram:
Activity 48.23
Inside the DO..LOOP structure of your last program, add the following lines:
IF SPACEKEY()
ODE DESTROY OBJECT 65
ENDIF
Test the new program by destroying the ODE volume before the ball hits the
blocks. How is the program affected?
{
X
real
In the diagram:
Activity 48.24
real
In the diagram:
The statement returns the length of the object along the world y-axis.
integer
The statement returns 1 if at least one message exists in the stack; otherwise zero is
returned.
Typical code for retrieving collision details from the stack would be:
B
( )
integer
In the diagram:
This statement should be used after a message has been retrieved from the collision
stack.
For example, we could see which objects have collided using the code:
Activity 48.25
{
X
{
The ODE GET A
OBJECT
VELOCITY ODE GET OBJECT VELOCITY Y ( )
Statement B
Z
real
In the diagram:
Activity 48.26
{
X
B
ANGULAR VELOCITY Y
Z
( )
real
In the diagram:
In the diagram:
The program in LISTING-48.8 allows the user to apply a force to a cube by pressing
the space bar.
LISTING-48.8 ScreenSetUp()
Applying a Force
REM *** Force to be applied ***
force# = 0.01
REM *** First time space bar pressed, apply force ***
IF SPACEKEY()=1 AND (NOT applied)
ODE ADD FORCE 2,force#,0,0,0,0,0
REM *** Remember force has been applied ***
applied=1
ENDIF
LOOP
FUNCTION ScreenSetUp()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(125,125,125)
BACKDROP ON
AUTOCAM OFF
POSITION CAMERA 0,10,-150
POINT CAMERA 0,0,0
ENDFUNCTION
Activity 48.27
Try modifying the value of force# and applying it to the y and z directions
instead of x.
Check out the effects of changing the point at which the force is applied.
Object 2
When two objects collide with each A temporary contact joint is created at The contact joint contains a contact
other... the point of contact. normal detailing a repelling force used
by the two objects after colliding.
Fdir1 Fdir2
If the two objects approach each other The friction between the two surfaces These friction vectors and the softness
at a narrow angle one may pass over is determined by two friction vectors of an object's structure can be set using
the surface of the other. Fdir1 and Fdir2. various surface contact statements.
There are a series of statements allowing the programmer to fine-tune how the
collision should be dealt with and the forces involved. We can set factors such as
the bounciness of the objects, and the surface friction components along both
directions (Fdir1 and Fdir2).
Exactly which options are to be used must first be selected using a set of ODE
SETSURFACE MODE CONTACT statements which are designed to
select/deselect various characteristics. These statements and a brief explanation of
what effect is being selected are shown in TABLE-48.1.
TABLE-48.1 Statement Parameters Description
ScreenSetUp()
LISTING-48.9
REM *** Start ODE ***
Creating Bounce
ODE START
ODE SET WORLD STEP 0.05
ODE SET WORLD ERP (0.2)*2.5
ODE SET WORLD CFM (10^-5)*2.5
ODE SET STEP MODE 0
CreateBounce(2)
DO
ODE UPDATE
LOOP
FUNCTION CreateBounce(objno)
ODE SETSURFACE MODE CONTACT BOUNCE objno,0
ODE SET CONTACT BOUNCE objno,0.7
ODE SETSURFACE MODE CONTACT SOFT ERP objno,0
ODE SET CONTACT SOFT ERP objno, 0.8
ODE SETSURFACE MODE CONTACT SOFT CFM objno, 0
ODE SET CONTACT SOFT CFM objno,200.5
ENDFUNCTION
Activity 48.28
Try varying the parameters of the statements in the new function and check
out what effect this has.
The next routine tests out the conveyer-belt effect created using the CONTACT
MOTION1 and CONTACT MOTION2 statements:
FUNCTION ConveyerBelt(objno)
ODE SET CONTACT FDIR1 objno,50
`ODE SETSURFACE MODE CONTACT MOTION1 objno,0
`ODE SET CONTACT MOTION1 objno,10
`ODE SETSURFACE MODE CONTACT MOTION2 objno,0
`ODE SET CONTACT MOTION2 objno,10
ENDFUNCTION
Activity 48.29
Add the code for ConveyerBelt() to your last program (notice that most of the
lines have been commented out at this stage).
In the main section of your program, comment out the call to CreateBounce()
and replace it with the lines:
Now remove the comments from lines 2 and 3 in ConveyerBelt() and run the
program again. What happens to the dice this time?
Next, re-comment out lines 2 and 3 and uncomment lines 4 and 5. How does
the dice react ?
Remove all comments in ConveyerBelt() and run the program again. What
path does the dice take?
l Use ODE CREATE DYNAMIC BOX to place a box volume around a moveable
object.
l Use ODE CREATE STATIC BOX to place a box volume around an immovable
object.
l Use ODE START before any other ODE statements in your program. This
initialises the ODE.
l Use ODE UPDATE in a loop structure to continually update the calculations and
movements performed by the ODE.
l Use ODE SET WORLD GRAVITY to modify the gravitational force used in a
program.
l Use ODE SET WORLD STEP to set the time interval assumed to have passed
between each ODE update.
l Use ODE SET WORLD ERP to specify the error production parameter.
l Use ODE SET WORLD CFM to set the bounciness of every ODE object.
l Use ODE SET CONTACT FDIR1 to set the surface friction of an object.
l Use ODE SET ANGULAR VELOCITY to set the angular velocity of an object.
l Use ODE GET BODY LINEAR VELOCITY to determine the linear velocity
of an object.
l Use ODE GET BODY HEIGHT to determine the height of an object. This may
change as an asymmetrical shape is rotated.
l Use ODE COLLISION GET MESSAGE to retrieve the latest entry in the
collision stack.
l Use ODE GET OBJECT to determine which two objects have collided. This
statement is used after an entry has been retrieved from the collision stack.
l Use ODE GET OBJECT VELOCITY to retrieve the velocities of the two objects
that have collided. This statement is used after an entry has been retrieved from
the collision stack.
No solution required. to
Activity 48.9
Activity 48.4
The whole process runs much more slowly at 0.05. Using
The code is: 0.2 speeds things up.
ScreenSetUp()
REM *** Make cube ***
MAKE OBJECT CUBE 1,10
Activity 48.10
POSITION OBJECT 1,0,25,0
No solution required.
REM *** Make ground ***
MAKE OBJECT PLAIN 2, 100,100
XROTATE OBJECT 2,90
WAIT KEY
Activity 48.11
REM *** Start Physics engine ***
ODE START
Change
ODE SET WORLD GRAVITY 0,-2,0
REM *** Link ODE box to cube and plane *** ODE CREATE STATIC BOX 2
ODE CREATE DYNAMIC BOX 1
ODE CREATE DYNAMIC BOX 2 to
REM *** Update screen ***
DO ODE CREATE STATIC TRIANGLE MESH 2
ODE UPDATE
LOOP Although it has a static volume, the cone sinks into the
REM *** End program ***
ground! The cube tilts on hitting the top of the cone.
ODE END
END
Activity 48.12
The plane which is meant to represent ground level also
falls! The ERP values have the following effects:
100,0,0 cube moves quickly to the right Since object 2 has a much larger mass, it has a greater
-10,0,0 cube moves slowly to the right effect on object 1's trajectory.
0,5,0 cube moves up
0,0,20 cube moves off into the screen With a slight gravity, the cubes fall as they move.
0,-10,20 cube moves down and in, then moves in
only
Activity 48.22
Activity 48.16 To move the camera about the scene, the DO..LOOP
structure should be changed to:
As the cube moves away it also falls, eventually hitting
the ground and sliding on until it falls over the edge of the DO
ground. ODE UPDATE
PointCameraUsingMouse(0)
MoveCameraUsingMouse(0,0.5)
Activity 48.17 LOOP
The cubes stop moving when they collide. Also, to include the necessary code, we need to add the line
#INCLUDE "camera.dba"
Activity 48.18
When object 1's velocity is set to 4,0,0, it pushes object 2 at the start of the program.
to the right.
Activity 48.23
When object 1's velocity is set to 4,-1,0, it tilts and halts
object 2. The main loop should now be:
DO
Activity 48.19 ODE UPDATE
IF SPACEKEY()
Object 1 tilts slightly, but does not continue to rotate. ODE DESTROY OBJECT 65
ENDIF
When the statement is moved within the DO..LOOP, the PointCameraUsingMouse(0)
cube continues to rotate as it moves. MoveCameraUsingMouse(0,0.5)
LOOP
Activity 48.24
Activity 48.21
To keep the main section of the program simple, it is
The final version of the main section of the program probably best if we do the hard work in two routines:
should now be:
FUNCTION DisplayVelocities()
ScreenSetUp() SET CURSOR 100,100
REM *** Make cubes *** PRINT "Velocity (object 1):",
MAKE OBJECT CUBE 1,7 Ä
VelocityDetails$(1)
POSITION OBJECT 1, -20,20,0 SET CURSOR 100,130
MAKE OBJECT CUBE 2,7 PRINT "Velocity (object 2):",
POSITION OBJECT 2,20,20,0 Ä
VelocityDetails$(2)
REM *** Wait to start ODE *** ENDFUNCTION
WAIT KEY
ODE START FUNCTION VelocityDetails$(objno)
REM *** Build ODE volumes *** x# = ODE GET BODY LINEAR VELOCITY
ODE CREATE DYNAMIC BOX 1 Ä
X(objno)
ODE CREATE DYNAMIC BOX 2 y# = ODE GET BODY LINEAR VELOCITY
The DO..LOOP should now be: On the third run, with lines 4 and 5 of ConveyerBelt()
included, the cube slides off into the screen.
DO
ODE UPDATE On the last run, with lines 2, 3, 4 and 5 of ConveyerBelt()
DisplayCollisions() included, the cube reverts to sliding off to the left.
LOOP
Activity 48.26
Again, we start by adding a helper function to perform all
the work
FUNCTION CollisionVelocityDetails$(op$)
IF op$ = "A"
x# = ODE GET OBJECT A VELOCITY X()
y# = ODE GET OBJECT A VELOCITY Y()
z# = ODE GET OBJECT A VELOCITY Z()
result$ = "X: "+STR$(x#)+" Y: "
Ä
+STR$(y#)+" Z: "+STR$(z#)
ELSE
x# = ODE GET OBJECT B VELOCITY X()
y# = ODE GET OBJECT B VELOCITY Y()
z# = ODE GET OBJECT B VELOCITY Z()
result$ = "X: "+STR$(x#)+" Y: "
Ä
+STR$(y#)+" Z: "+STR$(z#)
ENDIF
ENDFUNCTION result$
FUNCTION DisplayCollisions()
IF ODE COLLISION MESSAGE EXISTS()
ODE COLLISION GET MESSAGE
obj1 = ODE GET OBJECT A()
obj2 = ODE GET OBJECT B()
REPEAT
SET CURSOR 100,100
PRINT "Objects collided: ",obj1,
Ä" ",obj2
SET CURSOR 100,130
PRINT "Collision velocity "
ax = 12.3
In a graphical context, the term vector refers to a directed line that has both
magnitude and direction (see FIG-49.1).
The tail of
the vector
magnitude is denoted
by the length of the line The head of
the vector
The details of a vector can be stored as three numbers giving the position of the
head of the vector relative to its tail, as shown in FIG-49.2.
From the offset values held within the vector, its magnitude can be determined. For
vector a, its magnitude (written as ||a||) can be calculated using the formula:
FIG-49.4
The coordinates of
Storing a Point's a point in 3D space...
Coordinates in a 3D
Vector
12.3 4.6 1.7
...can be stored in
a 3D vector
3D Vector Statements
The MAKE VECTOR3 Statement
To create a 3D vector we need to use the MAKE VECTOR3 statement which has
the format shown in FIG-49.5.
FIG-49.5
The MAKE VECTOR3 MAKE VECTOR3 ( vectno )
Statement
integer
MAKE VECTOR3(1)
To help identify vectors within a program it's probably best, in all but the shortest
programs, to use a constant for the vector ID:
#CONSTANT myvector
MAKE VECTOR3(myvector)
In the diagram:
For example, we could assign the values 12.3, 4.6 and 1.7 to myvector using the
lines:
#CONSTANT myvector
MAKE VECTOR3(myvector)
SET VECTOR3 myvector,12.3,4.6,1.7
real
In the diagram:
For example, using the 3D vector we set up in the example above, the line
PRINT Y VECTOR3(myvector)
integer
In the diagram:
In the diagram:
For example, we could copy the contents of vector 2 to vector 1 using the line
In the diagram:
For example, 3D vector 1 could have its contents multiplied by 1.3 using the line:
In the diagram:
We could use this statement to store the result of multiplying the contents of vector
2 by 1.3 in vector 1 using the line:
In the diagram:
Activity 49.1
real
In the diagram:
Activity 49.2
Add to your previous program so that the vector is used to set the velocity of a
cube. The speed of the cube should be displayed.
real
In the diagram:
In the diagram:
#CONSTANT v1 1
#CONSTANT v2 2
#CONSTANT v3 3
ADD VECTOR3 v3,v1,v2
The obvious question at this point is; Why might we want to add two vectors?
Simply, if two thrust vectors are added, it gives us the resulting thrust (see
FIG-49.16).
A A A
B
A spaceship has fired its main engines It now uses its thrusters to rotate about It now fires its main engines again
giving it velocity A. its own axes, but continues to travel creating a thrust represented by B.
along the previous trajectory.
A
B B B
C
A A
To calculate the ship's new velocity This can be achieved visually by A line drawn from the start of A to the
we need to add velocities A and B. moving the start of velocity B to the end end of B represents the ship's new
of velocity A... velocity, C.
In the diagram:
If we assume vectors a and b contain coordinates of two points rather than velocities,
then the SUBTRACT VECTOR3 statement can be used to calculate the position of
point b relative to point a by subtracting vector a from vector b.
We can perform this operation in DarkBASIC Pro using the DOT PRODUCT
VECTOR3 statement which has the format shown in FIG-49.18.
FIG-49.18
The DOT PRODUCT DOT PRODUCT VECTOR3 ( vectno1 , vectno2 )
VECTOR3 Statement
real
In the diagram:
This operation is used when we need to discover the angle between two vectors (see
FIG-49.19).
FIG-49.19 b
θ a
The angle θ (theta) between vectors a and b can be calculated using the formula:
θ = acos
( a.b
||a|| ||b||
θ = ab
acos( . )
In the diagram:
integer
In the diagram:
vectno1, vectno2 are integer values giving the IDs of the two
vectors being compared.
If the contents of the two vectors are identical, the statement returns 1; otherwise
zero is returned.
x y z
In the diagram:
In the diagram:
In the diagram:
For example, using the vectors [0,0,100] and [0,10,0] (which are parallel to the z
and y axes respectively) the CROSS PRODUCT VECTOR3 statement would create
a new vector at right angles to these - that is a vector parallel to the x-axis. This is
demonstrated in LISTING-49.1.
LISTING-49.1
SET DISPLAY MODE 1280,1024,32
Creating a Cross Product r = MAKE VECTOR3(1)
Vector SET VECTOR3 1, 0,0,100
r = MAKE VECTOR3(2)
SET VECTOR3 2, 0,10,0
r = MAKE VECTOR3(3)
CROSS PRODUCT VECTOR3 3,1,2
x# = X VECTOR3(3)
continued on next page
Activity 49.3
Summary
l 3D vectors are structures which hold 3 real numbers.
l Use COPY VECTOR3 to copy the contents of one vector into another.
l Use DIVIDE VECTOR3 to divide the elements of a vector by a fixed value. The
result is stored in the named vector.
l Use ADD VECTOR3 to add the contents of two vectors and store the result in
a third vector.
l Use SUBTRACT VECTOR3 to subtract the contents of one vector from another
and store the result in a third vector.
l Use DOT PRODUCT VECTOR3 to calculate the dot product of two vectors.
l Use MAXIMIZE VECTOR3 to create a new vector from the maximum values
in two other vectors.
l Use MINIMIZE VECTOR3 to create a new vector from the minimum values in
two other vectors.
These vectors are used to store 3D coordinates or vectors, but a fourth element (w)
is added (see FIG-49.26) because some of the mathematics we need to perform
demand 4 element vectors, not 3.
FIG-49.26
A 4D Vector A 4D Vector is a
data structure which stores
4 real numbers
x y z w
The new element within the vector is known as w. We are free to assign any value
we wish to w.
bx = ax * w
by = ay * w
bz = az * w
bw = w
ax = bx / w
ay = by / w
az = bz / w
Activity 49.4
Many of the commands available for 4D vectors perform the same operations as
those already covered for 2 and 3 dimensional vectors, so rather than describe each
one separately they are listed below in TABLE-49.1.
[ 4
7
10
-42
8
Like vectors, matrices are often assigned names, usually shown in uppercase bold:
M=
[ 4
7
10
-42
8
Just like arrays, the individual elements within a matrix are identified by their row
and column positions. Some texts start subscripting from 1, hence the top-left
element is referred to as 1,1; others start subscripts at 0 and the top-left element is
then 0,0. We will subscript from 0,0 since DarkBASIC Pro arrays begin
subscripting at zero.
M=
[ m00
m10
m01
m11
m02
m12
DarkBASIC Pro has statements for creating and manipulating only 4 by 4 matrices.
The matrices in DarkBASIC Pro are really intended for use with shaders (see
Chapter 50) and hence have very limited accessibility. They can be created and
manipulated but are not designed to be examined in any way.
Matrix Statements
The MAKE MATRIX4 Statement
To create a 4 by 4 matrix we use the MAKE MATRIX4 statement which has the
format shown in FIG-49.27.
FIG-49.27
MAKE MATRIX4 ( matno )
The MAKE MATRIX4
Statement
integer
In the diagram:
You could be forgiven for thinking you can now set up any values you want within
the matrix you've just created, but, in fact, DarkBASIC Pro matrix objects are
designed to hold only specific values which are loaded into the matrix using
tailor-made statements.
[
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
Notice the diagonal line of 1s - this is the sign of an identity matrix, no matter what
size the matrix is, although it must be square.
We can set the contents of a matrix to the identity matrix using the SET IDENTITY
MATRIX4 statement which has the format shown in FIG-49.28.
FIG-49.28
The SET IDENTITY SET IDENTITY MATRIX4 matno
MATRIX4 Statement
In the diagram:
FIG-49.29
IS IDENTITY MATRIX4 ( matno )
The IS IDENTITY
MATRIX4 Statement
integer
In the diagram:
For example, we could load the world coordinates of a polygon into matrix 1using
the line:
WORLD MATRIX4 1
In the diagram:
integer
In the diagram:
matno1, matno2 are integer values giving the IDs of the two
matrices being compared.
If the contents of the two matrices are identical, the statement returns 1; otherwise
zero is returned.
In the diagram:
[ [ [
a00 a01 a02 a03 b00 b01 b02 b03 a00+b00 a01+b01 a02+b02 a03+b03
a10 a11 a12 a13 b10 b11 b12 b13 a10+b10 a11+b11 a 12+b12 a13+b13
A= B= A +B =
a20 a21 a22 a23 a20+b20 a21+b21 a22+b22 a23+b23
b20 b21 b22 b23
a30 a31 a32 a33 a30+b30 a31+b31 a32+b32 a33+b33
b30 b31 b32 b33
In the diagram:
{
FIG-49.35
, matno1 , matno2
The MULTIPLE MULTIPLY MATRIX4 destmatno
MATRIX4 Statement
, multiplier
In the diagram:
Option 1:
[ [ [
a00 a01 a02 a03 b00 b01 b02 b03 c00 c01 c02 c03
a10 a11 a12 a13 b10 b11 b12 b13 c10 c11 c12 c13
A= B= C =
a20 a21 a22 a23 c20 c21 c22 c23
b20 b21 b22 b23
a30 a31 a32 a33 c30 c31 c32 c33
b30 b31 b32 b33
What row and column would be used to calculate the value at position c21?
Calculating the inverse of a given matrix is more complex, but when that inverse is
multiplied by the original matrix, the result is the identity matrix. Just to make life
a little more complicated, not every matrix has an inverse!
We can attempt to calculate the inverse of a specified matrix using the INVERSE
MATRIX4 statement whose format is shown in FIG-49.37.
FIG-49.37
The INVERSE
INVERSE MATRIX4 ( matno1 , matno2 )
MATRIX4 Statement
integer
In the diagram:
In the diagram:
For example, if matrix 1 held the coordinates of a triangle, we could double its size
in all dimensions using the line:
In the diagram:
FIG-49.40
{{
The ROTATE
X
MATRIX4 Statement
y MATRIX4 matno , angle
ROTATE
z
In the diagram:
Option 1:
Option 2:
YPR Use this option to rotate about more than one axis.
yaw, pitch, roll are the angles of rotation about the y, x and z
axes respectively (note the order!).
[ [
1 2 3 4 1 5 9 13
5 6 7 8 2 6 10 14
A= AT =
9 10 11 12 3 7 11 15
13 14 15 16 4 8 12 16
In the diagram:
integer
In the diagram:
As stated already, the sole purpose of the matrix data structure is to be passed to a
shader to modify the overall appearance of the 3D objects displayed on the screen.
Summary
l A mathematical matrix is a grid of values.
l Use SET IDENTITY MATRIX4 to set the contents of a matrix to the identity
matrix.
l Use ADD MATRIX4 to add two matrices and store the result in a third matrix.
l Use SUBTRACT MATRIX4 to subtract one matrix from another and store the
result in a third matrix.
l Use MULTIPLY MATRIX4 to multiply two matrices and store the result in a
third matrix or to multiply a matrix by a specified value.
l Use SCALE MATRIX4 to scale the coordinate values held within a matrix.
l Use TRANSPOSE MATRIX4 to switch the rows and columns within a matrix.
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
ENDFUNCTION
FUNCTION Read3Values()
INPUT "Enter first value : ", v1#
INPUT "Enter second value : ", v2#
INPUT "Enter third value : ", v3#
ENDFUNCTION
FUNCTION DisplayVector(vector)
x# = X VECTOR3(vector)
y# = Y VECTOR3(vector)
z# = Z VECTOR3(vector)
PRINT "VECTOR ",vector," : ", x#,
Ä
" ",y#," ",z#
ENDFUNCTION
Activity 49.2
The main section of the program should be
changed to:
GLOBAL v1#,v2#,v3#
SetUpScreen()
Read3Values()
REM *** Set up Vector ***
r = MAKE VECTOR3(1)
SET VECTOR3 1,v1#,v2#,v3#
REM *** Create cube ***
MAKE OBJECT CUBE 1,10
POINT OBJECT 1, X VECTOR3(1),
ÄY VECTOR3(1),Z VECTOR3(1)
DO
IF UPKEY()
ModifyVector(1,10)
ENDIF
IF DOWNKEY()
ModifyVector(1,-10)
ENDIF
speed# = LENGTH VECTOR3(1)
MOVE OBJECT 1,speed#
SET CURSOR 100,100
PRINT "Speed : ",speed#
LOOP
REM *** End program ***
WAIT KEY
END
Creation of a shader is beyond the scope of this text since it involves learning a
whole new language, but DarkBASIC Pro has commands to load and apply these
files.
Ø vertex shaders
Ø pixel shaders
Several shaders can also be placed in a special effects file (FX file) so that their
effects can be combined.
Vertex Shader
Graphical data sent to your graphics card goes through a great deal of processing
before the final image appears on the screen. One of the earliest stages is the vertex
shader which is a set of commands that manipulates the vertices of the the polygons
that make up a 3D shape. By manipulating these vertices, a 3D object can be
transformed, deformed or morphed. Lighting and texturing can also be altered
within a vertex shader.
Pixel Shader
Once the 3D objects which make up the visible part of your world have been
remapped onto a 2D plane for display on your monitor, a pixel shader can modify
the colour of each individual pixel on the screen. This allows further lighting and
texturing effects to be applied as well as other options such as bump mapping. As
FX Files
An FX file is a Microsoft DirectX effects file and it not only contains both vertex
and pixel shaders, but also can define constants, parameters and texture files used
by those shaders.
real
The statement returns a real number giving the version number of the vertex shader
used by your graphics card. We can display that version number using the line:
real
The statement returns a real number giving the version number of the pixel shader
used by your graphics card. To display the version number use the line:
Activity 50.1
Write a short program (act5001.dbpro) to display the vertex and pixel shaders'
version numbers available on your graphics card.
FX Statements
The easiest way to get started with shaders is to make use of FX files which already
contain all the necessary details to execute vertex and pixel shaders.
In the diagram:
For example, we could load the effects file bubble.fx and use the textures it defines
with the line:
integer
In the diagram:
The PERFORM
CHECKLIST FOR
PERFORM CHECKLIST FOR EFFECT ERRORS effno
EFFECT ERRORS
Statement
After this statement has been executed, we need to use the standard checklist
statements to access the contents of the list.
In the diagram:
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 0,5,-50
ENDFUNCTION
Activity 50.2
FIG-50.7
SET EFFECT ON objno , filename , textureflag
The SET EFFECT ON
Statement
In the diagram:
Activity 50.3
Modify your last program to use the SET EFFECT ON statement in place of
LOAD EFFECT and SET OBJECT EFFECT.
Although the SET EFFECT ON statement might appear to be the easier method of
applying an effect it is, in fact, more inefficient since a new copy of the effect needs
to be loaded for each object to which it is applied, whereas using LOAD EFFECT
FIG-50.8
The DELETE EFFECT DELETE EFFECT effno
Statement
In the diagram:
In the diagram:
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 0,5,-20
ENDFUNCTION
Activity 50.5
Write a program (act5005.dbpro) to display the values that may be set in the
bubble.fx effects file.
Actually, we need more information about these named values within the FX file.
Specifically, we need to know each value's type (integer, real, etc.) and if it can have
its value changed from within a DarkBASIC Pro program.
Not all variables can actually be modified. Those that can, have their CHECKLIST
VALUE B set to 1. And even this is not an end of the limitations; DarkBASIC Pro
does not contain a statement which will allow us to assign a new value to values of
an undefined type.
Activity 50.6
Modify your last program to display only those values which are not of an
undefined type and can be modified.
{
BOOLEAN
INTEGER
VECTOR
MATRIX
In the diagram:
BOOLEAN,INTEGER,FLOAT,VECTOR,MATRIX
Use the option appropriate to the type of value
being assigned.
For example, the bubbles.fx file has a float parameter named ticks. This could have
Actually, it's not very its value set to 8.5 using the lines:
smart to change ticks
since it represents the LOAD EFFECT "bubble.fx",1,0
time, but the instruction SET EFFECT CONSTANT FLOAT 1,"ticks",8.5
itself is valid.
In the diagram:
In the diagram:
To make use of a shader, we must load the file into a vertex shader object using the
CREATE VERTEX SHADER FROM FILE statement which has the format shown
in FIG-50.14.
In the diagram:
In the diagram:
In the diagram:
In the diagram:
A vertex shader may require data in the form of a vector or a matrix. Such
information can be sent to the vertex shader using the following statements.
Summary
l Shaders are used to modify the final image that appears on the screen.
l Vertex shaders can modify the coordinates, shading and texturing of individual
vertices.
l Pixel shaders can modify the colour of pixels before they appear on screen.
l FX files can contain both vertex and pixel shaders as well as other code.
l The shader capabilities of your system are determined by your graphics card.
FX Files
l Use LOAD EFFECT to load an FX file.
Shader Files
l Vertex and pixel shaders can exist independently of an effects file.
l Vertex and pixel shaders can be loaded and applied to specific objects.
Activity 50.3
MAKE OBJECT SPHERE 1, 10
LOAD IMAGE "lava1.bmp",1
TEXTURE OBJECT 1,1
REM *** Load and apply effect ***
SET EFFECT ON 1,"bubble.fx",0
IF EFFECT EXIST(1) = 0
PERFORM CHECKLIST FOR EFFECT ERRORS
FOR c = 1 TO CHECKLIST QUANTITY()
PRINT CHECKLIST STRING$(c)
NEXT c
WAIT KEY
END
ENDIF
REM *** end program ***
WAIT KEY
END
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 0,5,-50
ENDFUNCTION
Activity 50.4
No solution required.
Activity 50.5
SetUpScreen()
REM *** Load effect file ***
LOAD EFFECT "bubble.fx",1,0
REM *** Display parameters ***
PERFORM CHECKLIST FOR EFFECT VALUES 1
FOR c = 1 TO CHECKLIST QUANTITY()
PRINT CHECKLIST STRING$(c)
NEXT c
REM *** end program ***
WAIT KEY
END
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
AUTOCAM OFF
POSITION CAMERA 0,5,-20
ENDFUNCTION
Activity 50.6
SetUpScreen()
REM *** Load effect file ***
LOAD EFFECT "bubble.fx",1,0
REM *** Display parameters ***
PERFORM CHECKLIST FOR EFFECT VALUES 1
This setup gives us a lot more scope for a game; we can hide information from other
players, the players can be half a world apart, and playing against another human
being is always more fun than trying to best a few thousand IF statements.
Hardware Requirements
To get computers to communicate with each other, they need to be connected in
some way. If only two machines are involved and they are located in the same room,
then a simple cable connecting the machines' serial ports will be sufficient. Over a
longer distance two machines could communicate using a phone line and modems.
But when more machines are involved, then they need to be part of a network.
A local area network (LAN) is one where the computers are in close physical
proximity to each other; perhaps in the same room, building or campus.
A wide area network (WAN) is one where the linked computers are spread over
greater distances, possibly even in different countries.
In setting up a network there are choices to be made in how the machines are to
communicate with each other.
In a peer-to-peer network, each machine has equal status. Information from one
machine is set along a common connection and collected by the machine for which
it was destined. Every machine in the network has its own unique address and the
addresses of both the source computer and destination computer are sent as part of
the information transmitted.
In a client/server setup, one machine acts as a server and the others as clients. The
server has links to every client. While the server can communicate directly with any
client, communication between clients must be routed through the server.
The client machines send requests for data to the server and the server sends back
the necessary information.
The programs in this chapter have been tested on a LAN using a peer-to-peer setup.
Peer-to-peer is the default setup for Microsoft Windows.
Getting Started
During the early testing stages, it is likely that we will be running our programs
over machines located in the same room, since this will make testing much easier -
especially if you're working alone! We need to start by making sure that all the
computers that are to be used are switched on and communicating with each other.
Activity 51.1
FIG-51.2 shows a screen dump of a typical output from the program above.
FIG-51.2
The Output Produced by
a Connections Check
The IPX connection is usually installed when you have a network card (Ethernet)
installed.
When we choose which connection we will be using in our program, we use the
corresponding number given in the listing. For example, if a machine created the
list shown above, then we would use option 4 to create a TCP/IP connection between
the machines. However, TCP/IP will not be listed as option 4 on every computer;
perhaps the IPX option is not available on your machine; in this case, TCP/IP will
be listed as option 3.
TCP/IP
Transmission Control Protocol/Internet Protocol is nothing more than an agreed
set of standards for transmitting digital information on the Internet. The same
standard is also used when transmitting data over most networks, even when there
is no access to the Internet itself.
Ø TCP - which ensures the correct delivery of data from one machine to
another. It can detect errors in transmission and cause retransmission
until the data is successfully received.
Ø IP - which is responsible for moving data from machine to machine. The
data is organised into packets with each packet including 4 bytes of
information giving the address of the machine to which that packet is to
be sent (it's a bit like an address on an envelope). Every machine on a
network will have a different IP address.
Activity 51.2
In this Activity you are going to discover the IP address of your own machine.
On one of the machines you are using, create a command line window
(START:RUN: Enter cmd).
FIG-51.3
The SET NET
SET NET CONNECTION connecttype
[ , address
CONNECTION
Statement
In the diagram:
Now the main section of the program can create a connection using just 2 lines:
TCPIP = GetTCPIPNumber()
SET NET CONNECTION TCPIP,"192.168.0.1"
In the diagram:
The max value determines how many machines (including the host) can be part of
the link at any one time. Although a maximum of 256 is possible, a much smaller
figure will probably be more practical. The maximum number of players will be
determined by the speed of the game. This, in turn, is determined by the speed of
Activity 51.4
In your last program, just before the end of the main section, add the lines
CREATE NET GAME "Passing Messages","John",2,1
PRINT "Program hosted"
SYNC
Like the host, the client will have to detect what connection options are available
and choose a TCP/IP connection, but it will be joining rather than creating a game,
so it must start by discovering what games are available (that is, what games are
being hosted on other machines).
Only programs hosted on other machines will be listed - programs hosted on the
machine executing this statement do not appear in the list.
FUNCTION ListNetSessions()
PRINT "Sessions available : "
PERFORM CHECKLIST FOR NET SESSIONS
FOR c = 1 to CHECKLIST QUANTITY()
PRINT c,". ",CHECKLIST STRING$(c)
NEXT c
PRINT "List complete"
ENDFUNCTION
Notice that the SET NET CONNECTION statement gives the IP address of the host
machine, not the client machine's own IP address.
Activity 51.5
On your second machine (the client machine) type in and run the program in
LISTING-51.3 (client01.dbpro).
The listing produced includes two numbers at the end of each entry in the list. The
entry beside Passing Messages should be (1/2). This tells us that the program can
have up to 2 participants and that it already has 1 (the host).
FIG-51.6
The JOIN NET GAME JOIN NET GAME session , player
Statement
In the diagram:
For example, assuming the client machine has listed Passing Messages as the first
item in its net sessions list, then the user at the client machine can participate in that
program (using the name Rog) by executing the line:
User's Name is the name the player used when joining the
session.
FIG-51.8 shows the player details produced for a session with John (host), Rog, Liz
and Mary.
Activity 51.6
On your client machine, modify client01.dbpro, so that the user joins Passing
Messages using the name Rog.
Add a function, ListPlayersDetails(), that lists the details of all players in the
current session.
Add a call to this function near the end of the main section and test the
program. What numbers are assigned in the list to the two players?
Activity 51.7
IF
host:
client:
Join session
ENDIF
FUNCTION EstablishConnection()
TCPIPno = GetTCPIPNumber()
IPaddress$ = GetIPAddress$()
REM *** Select TCP/IP connection ***
SET NET CONNECTION TCPIPno,IPaddress$
option = HostOrClient()
IF option = HOST
REM *** Host program ***
INPUT "Enter the name you wish to use : ",name$
INPUT "Enter maximum users : ",max
INPUT "Enter session name : ", session$
CREATE NET GAME session$,name$,max,1
ELSE
ListNetSessions()
session = GetSessionToJoin()
INPUT "Enter the name you wish to use : ",name$
JOIN NET GAME session,name$
ListPlayersDetails()
ENDIF
ENDFUNCTION option
#CONSTANT HOST 1
#CONSTANT CLIENT 2
FUNCTION GetTCPIPNumber()
result = 0
PERFORM CHECKLIST FOR NET CONNECTIONS
FOR c = 1 to CHECKLIST QUANTITY()
IF LEFT$(CHECKLIST STRING$(c),8) = "Internet"
result = c
ENDIF
NEXT c
FUNCTION ListNetSessions()
PRINT "Sessions available : "
PERFORM CHECKLIST FOR NET SESSIONS
FOR c = 1 to CHECKLIST QUANTITY()
PRINT c," ",CHECKLIST STRING$(c)
NEXT c
ENDFUNCTION
FUNCTION ListPlayersDetails()
PRINT "Players in session "
PERFORM CHECKLIST FOR NET PLAYERS
FOR c = 1 to CHECKLIST QUANTITY()
PRINT c," Name: ",CHECKLIST STRING$(c)," Local ID : ",
Ä
CHECKLIST VALUE A(c)," Universal ID : ",
Ä
CHECKLIST VALUE B(c)," Me? : ",CHECKLIST VALUE C(c),
Ä
" Host? ",CHECKLIST VALUE D(c)
NEXT c
ENDFUNCTION
FUNCTION HostOrClient()
PRINT "Choose from the following options : "
PRINT " 1 - Host a new session"
PRINT " 2 - Join an existing session"
INPUT "Enter option : ",option
WHILE option < 1 OR option > 2
PRINT "Enter 1 or 2"
INPUT "Enter option : ",option
ENDWHILE
ENDFUNCTION option
FUNCTION GetIPAddress$()
INPUT "Enter the IP address of the HOST machine
Ä(xxx.xxx.xxx.xxx) : ",IPaddress$
ENDFUNCTION IPaddress$
FUNCTION GetSessionToJoin()
INPUT "Enter session you wish to join : ",session
WHILE session < 1
PRINT "Enter the session number from the list given"
INPUT "Enter option : ",session
ENDWHILE
ENDFUNCTION session
Activity 51.8
{
INTEGER
The SEND NET
MESSAGE
Statement SEND NET MESSAGE FLOAT player , value
STRING
In the diagram:
For example, if we assume the host machine's player (John) wants to send the
message "Hello there" to Rog's client machine, and that Rog has, when listed on
John's machine, the local ID of 2, then the required statement that should be added
to the host program is:
On the other hand, if Rog wants to send John a message, since, from Rog's
perspective, he has a local ID of 1 and John's ID is 2, a message from Rog to John
would also use 2 as the first parameter:
FIG-51.11
NET MESSAGE EXISTS ( )
The NET MESSAGE
EXISTS Statement
integer
If we want a program to wait for a message arriving ,we can use the following code:
REPEAT
GET NET MESSAGE
UNTIL NET MESSAGE EXISTS()
{
FIG-51.12 INTEGER
The NET MESSAGE
Statement NET MESSAGE FLOAT ( )
STRING$
value
In the diagram:
The type of value returned by this statement will match the type name used. That
is, the statement will return an integer, real (float), or string.
Activity 51.9
Copy the .exe version of the program onto the client machine and check that
the two users can communicate with each other.
integer
The statement returns the local ID (as seen from the receiver's perspective) of the
user that sent the message which has been received.
integer
Activity 51.10
Modify your last program to display the local ID of the sender of each
message.
{
BITMAP
IMAGE
MESH
MEMBLOCK
In the diagram:
For example, to transmit the details of a cube to all other machines in a session, we
could use the following lines:
{
BITMAP
The NET MESSAGE
Statement
IMAGE
MESH
MEMBLOCK
value
In the diagram:
In the next program (LISTING-51.5) we are going to send images between the host
and client.
LISTING-51.5
#INCLUDE "NetConnect.dba"
Sending an Image REM *** Use a window ***
SET WINDOW ON
SET WINDOW SIZE 500,900
SET WINDOW POSITION 0,0
r = EstablishConnection()
REM *** Load images ***
LOAD IMAGE "image1.jpg",1
LOAD IMAGE "image2.bmp",2
DO
REM *** Get recipient and image ***
INPUT "Choose number of player to receive message ",playerno
Activity 51.11
integer
This statement returns an integer value based on the type of the latest message to
be received using the GET MESSAGE statement. The value returned is coded as
shown in TABLE-51.2.
CASE 6 `bitmap
value = VAL(value$)
SEND NET MESSAGE BITMAP recipient,value,1
ENDCASE
CASE 7 `sound
value = VAL(value$)
SEND NET MESSAGE SOUND recipient,value,1
ENDCASE
CASE 8 `mesh
value = VAL(value$)
SEND NET MESSAGE MESH recipient,value,1
ENDCASE
ENDSELECT
ENDFUNCTION
Next we need a function to receive a message, recognise the type of message and
display (or play) the message. This function, ReceiveMessage(), is coded as:
FUNCTION ReceiveMessage()
messagetype = NET MESSAGE TYPE()
messagefrom = NET MESSAGE PLAYER FROM()
SELECT messagetype
CASE 1 `integer
PRINT NET MESSAGE INTEGER()
ENDCASE
CASE 2 `real
PRINT NET MESSAGE FLOAT()
ENDCASE
CASE 3 `string
PRINT NET MESSAGE STRING$()
ENDCASE
CASE 4
NET MESSAGE MEMBLOCK 2
ENDCASE
CASE 5
NET MESSAGE IMAGE 2
PASTE IMAGE 2,0,0
DELETE IMAGE 2
ENDCASE
CASE 6
NET MESSAGE BITMAP 2
COPY BITMAP 2,0
DELETE BITMAP 2
ENDCASE
CASE 7
NET MESSAGE SOUND 2
PLAY SOUND 2
WAIT KEY
DELETE SOUND 2
ENDCASE
CASE 8
Activity 51.12
In the next program (see LISTING-51.6) we'll demonstrate more flexible message
passing where the users can choose the type of message that is to be transmitted.
LISTING-51.6 #INCLUDE "NetConnect.dba"
Passing Messages of
Various Types REM *** Use a window ***
SET WINDOW ON
SET WINDOW SIZE 500,900
SET WINDOW POSITION 0,0
EstablishConnection()
REM *** Get recipient and message ***
PRINT "Loading..."
LOAD IMAGE "image1.jpg",1
PRINT "bottlebrush"
LOAD BITMAP "image2.jpg",1
SET CURRENT BITMAP 0
PRINT "Cactus"
LOAD SOUND "welcome.wav",1
PRINT "Sound"
REM *** Send message ***
DO
INPUT "Choose number of player to receive message ",playerno
INPUT "Which type of message (1..8) :", messagetype
INPUT "Message value : ",value$
SendMessage(playerno,messagetype,value$)
GET NET MESSAGE
WHILE NET MESSAGE EXISTS()
ReceiveMessage()
GET NET MESSAGE
ENDWHILE
LOOP
REM *** End program ***
WAIT KEY
END
Activity 51.13
integer
If the statement returns zero, this means that no messages are currently awaiting
transmission. So, we could check how many messages are currently in the queue
using a statement such as:
Session Dynamics
If you were writing a multi-player on-line game, you'd expect new players to arrive
and existing players to leave during the game. To handle this we need statements
which detect and handle such events.
integer
If no new player has joined, the statement returns the value zero.
Every time a player joins a session, his details will be added as the first entry in the
checklist created by the PERFORM CHECKLIST FOR NET PLAYERS statement.
integer
FUNCTION MaintainPlayerList()
PERFORM CHECKLIST FOR NET PLAYERS
noofentries = CHECKLIST QUANTITY()
FOR c = 1 TO noofentries
players(c).id = CHECKLIST VALUE A(c)
players(c).name$ = CHECKLIST STRING$(c)
NEXT c
ENDFUNCTION
FUNCTION DisplayPlayerList()
FOR c = 1 TO noofentries
PRINT players(c).id," ",players(c).name$
NEXT c
ENDFUNCTION
Activity 51.14
John (host)
Rog (client)
Liz (client)
Mary (client)
Modify the program to display the name of any player leaving the session. The
player list should also be updated and displayed when a player leaves.
To test the new version of the program, create the same four users (as listed
earlier in the question) and have them leave the session (by closing the
appropriate Window) in the following order: Rog, Mary, Liz.
integer
This statement will return 1 if the current user has become the new host for a session;
otherwise zero is returned.
Activity 51.15
Modify your last program so that the message "You are new host" is displayed
by any user who becomes the new host for a session.
To test the program, create John, Rog, Liz, and Mary as before.
Close Liz.
Activity 51.16
IF ESCAPEKEY()
EXIT
ENDIF
integer
In the diagram:
In the diagram:
Although it is unlikely that you'll need to use CREATE NET PLAYER or FREE
NET PLAYER, we can demonstrate their use by adding the following code to the
main section of our previous program:
REM *** Add new player (Enter key) ***
IF RETURNKEY()
id = FindPlayerName("Avril")
IF id = 0
CREATE NET PLAYER ("Avril")
ENDIF
ENDIF
Activity 51.17
Using the code given above, modify your previous program and check that the
new user can be successfully added and removed.
FIG-51.25
The NET GAME
NET GAME EXISTS ( )
EXISTS Statement
integer
The statement returns 1 if the current game is still being hosted on a server;
otherwise zero is returned.
integer
The statement returns 1 if the current session no longer has a host; otherwise zero
is returned.
Summary
l Networking involves communication between two or more machines.
l Use SEND NET MESSAGE to send data to another machine in the current
session.
l Use NET MESSAGE PLAYER FROM to get the ID of the player from whom
the latest message was received.
l Use NET MESSAGE PLAYER TO to get the ID of the player to whom the last
message was directed.
l Use NET MESSAGE TYPE to determine what type of data is in the latest
message to be received.
l Use NET PLAYER CREATED to detect a new user joining the current session.
l Use NET GAME NOW HOSTING to discover if your machine is the host of
the current session.
l Use FREE NET PLAYER to withdraw a pseudo-user from the current session.
l Use NET GAME EXISTS to check that the server is still active in a client/server
setup.
l Use NET GAME LOST to detect when a session no longer has a host.
FIG-51.27
Gomuku
The game will be implemented using sprites. The board will be a sprite, as will each
X and O symbol placed on the board.
A Non-Networked Version
We'll start by creating a non-networked version of the game and then add in the
networking requirements later once we have the game itself working.
Program Data
We need to set up named constants for such things as the images and sprites used
in the game. Also we need a record structure defined to store details about a player
- a player ID number and the ID of the image used by that player (X image or O
image). Finally, we need a set of global variables to hold the current state of the
board, the position of the latest move and the ID to be used for the next sprite placed
on the screen.
REM *********************************
REM *** Types ***
REM *********************************
TYPE PlayerType
value
imgID
ENDTYPE
REM *********************************
REM *** GLOBALS ***
REM *********************************
GLOBAL DIM board(19,19)
GLOBAL nextsprite = 2
GLOBAL totalO
GLOBAL totalX
GLOBAL me AS PlayerType
GLOBAL you AS PlayerType
GLOBAL move
Game Logic
The game requires the following logic:
Set up screen
Set up board
REPEAT
ENDIF
End Game
Notice that the value returned from the first call to GetMove() is negated. This means
won will be -1 if X wins and 1 if O wins. We can make use of this to choose which
message should be displayed by EndGame().
Create a basic program from the code given, adding level 1 test stubs for each
function called.
Adding SetUpPlayerDetails()
The two global variables, me and you, are designed to contain details about each
player. The value field is used to assign a value to the board array in the cell
equivalent to where the player has placed his symbol (X or O); the imgID field is
used to identify which image is to be used when creating a sprite on the screen
representing the player's symbol.
The routine takes a value representing the parameter for me.value - all other
assignments can be calculated from this. The code for the routine is:
FUNCTION SetUpPlayerDetails(r)
REM *** Record my details ***
me.value = r
me.imgID = r
REM *** Record opponents details ***
you.value = 3-r
you.imgID = 3-r
ENDFUNCTION
Adding SetUpScreen()
The SetUpScreen() function is a typical routine for setting the screen resolution and
colour. Its code is:
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(100,50,50)
BACKDROP ON
ENDFUNCTION
Adding SetUpBoard()
The SetUpBoard() function loads the images used in the game (board, X symbol
and O symbol) and displays the board using a sprite. The code is:
FUNCTION SetUpBoard()
LOAD IMAGE "board2.bmp",boardimg
LOAD IMAGE "x.bmp",ximg
LOAD IMAGE "o.bmp",oimg
SPRITE boardspr,200,200,boardimg
ENDFUNCTION
Adding GetMove()
The GetMove() function takes as a parameter an indication of which player is to add
a symbol to the board. It then calls either GetMyMove() or GetOpponentsMove() as
appropriate. It also checks for a winning line of 5 symbols. The code for GetMove()
is:
FUNCTION GetMove(player)
IF me.value = player
GetMyMove()
Adding GetMyMove()
The GetMyMove() function gets the player's "move" and updates the screen by
adding an X or O sprite on the board and updating the board array.
Reading the mouse pointer's position to determine where on the board a symbol is
to be added, is actually performed by another function, GetSquare(), which returns
the row and column of the square selected as a single integer value.
FUNCTION GetMyMove()
REM *** Get a valid move ***
REPEAT
move = GetSquare()
row = move /100
col = move mod 100
result = board(row,col)
UNTIL result = 0
REM *** Add sprite to screen ***
SPRITE nextsprite,col*33+203,row*33+203,me.imgID
REM *** Update board array ***
board(row,col) = me.value
REM *** next sprite ID to be used is incremented ***
INC nextsprite
ENDFUNCTION
Adding GetSquare()
The GetSquare() function reads the position of the mouse pointer over the board.
Each square on the board is 33 pixels by 33 pixels, so the mouse coordinates are
rounded to a multiple of 33. The REPEAT..UNTIL structure ensures that the mouse
has been clicked within the board area.
The coordinates are converted to row and column values and these are combined
into a single value. For example, if the player clicked in row 12 column 8, this would
be converted to the single value 1208. It is this single value which is returned by
the function.
FUNCTION GetSquare()
REM *** Get mouse position within board ***
REPEAT
WAIT MOUSE
x1 = (MOUSEX()-200)/33 * 33
y1 = (MOUSEY()-200)/33 * 33
UNTIL InRange(x1,0,600) AND InRange(y1,0,600)
REM *** Convert to row and column ***
col = x1/33
row = y1/33
REM *** Convert row and column to a single value ***
result = row*100+col
ENDFUNCTION result
Adding InRange()
The InRange() function checks to see if a value is within a given range. If it is, 1 is
returned, otherwise zero is returned.
Adding GetOpponentsMove()
The GetOpponentsMove() function is almost identical to GetMyMove(), but uses
the you variable rather than me.
FUNCTION GetOpponentsMove()
REM *** Get a valid move ***
REPEAT
move = GetSquare()
row = move /100
col = move mod 100
result = board(row,col)
UNTIL result = 0
REM *** Add sprite ***
SPRITE nextsprite,col*33+203,row*33+203,you.imgid
REM *** Update board ***
board(row,col)= you.value
REM *** Another sprite used ***
INC nextsprite
ENDFUNCTION
Activity 51.19
Add all the code given so far and any additional test stubs required.
Test the program to make sure it accepts each player's move correctly.
Adding CheckForWin()
We need to check for a winning line of 5 symbols. This can be in a horizontal,
vertical or diagonal direction (see FIG-51.28).
FIG-51.28
A line can be Created in
any One of Four
Directions
FUNCTION CheckForWin(player)
result = 0
result = HorizontalCheck(player)
IF result = 0
result = VerticalCheck(player)
ENDIF
IF result = 0
result = BLTRDiagonalCheck(player)
ENDIF
IF result = 0
result = TLBRDiagonalCheck(player)
ENDIF
ENDFUNCTION result
FUNCTION HorizontalCheck(player)
REM *** Calc row and col of last move ***
row = move / 100
col = move mod 100
REM *** Upper and lower limit of search area ***
start = col - 4
IF start < 0
start = 0
ENDIF
finish = col + 4
IF finish > 18
finish = 18
ENDIF
REM *** search ***
total = 0
FOR col = start TO finish
IF board(row,col)= player
INC total
IF total = 5
EXIT
ENDIF
ELSE
total = 0
ENDIF
NEXT col
IF total = 5
result = 1
ELSE
result = 0
ENDIF
ENDFUNCTION result
FUNCTION VerticalCheck(player)
REM *** Calc row and col of last move ***
row = move / 100
col = move mod 100
REM *** Upper and lower limit of search area ***
start = row - 4
IF start < 0
start = 0
ENDIF
finish = row + 4
FUNCTION BLTRDiagonalCheck(player)
REM *** Calc row and col of last move ***
row = move / 100
col = move mod 100
REM *** Upper and lower limit of search area (row)***
srow = row
scol = col
FOR p = 1 TO 4
IF srow = 18 OR scol = 0
EXIT
ENDIF
INC srow
DEC scol
NEXT p
frow = row
fcol = col
FOR p = 1 TO 4
IF frow = 0 OR fcol = 18
EXIT
ENDIF
DEC frow
INC fcol
NEXT p
range = fcol-scol+1
FOR p = 1 TO range
IF board(srow,scol)= player
INC total
IF total = 5
EXIT
ENDIF
ELSE
total = 0
ENDIF
DEC srow
INC scol
NEXT p
REM *** Determine result ***
IF total = 5
result = 1
ELSE
result = 0
ENDIF
ENDFUNCTION result
Adding EndGame()
A winning message is supplied by the EndGame() function which uses the
parameter won to decide on which message is to be displayed.
FUNCTION EndGame(won)
REM *** Find out who won ***
IF won = -1
winner$ = "X"
ELSE
winner$ = "O"
ENDIF
REM *** Display message for 5 seconds ***
SET TEXT SIZE 40
now = TIMER()
WHILE TIMER() - now < 5000
SET CURSOR 1000,500
PRINT winner$," wins!!!"
ENDWHILE
ENDFUNCTION
Add the search and end game functions to your code and test the final program.
Before we start modifying our code, we need to copy the NetConnect.dba file we
created earlier in the chapter into our game folder.
Now the main logic needs to be changed so that host and client connections are
established and that the game waits for both players to be connected before
continuing. The new code is:
r = EstablishConnection()
WaitForSecondPlayer()
SetUpPlayerDetails(r)
SetupScreen()
SetUpBoard()
REPEAT
won = -GetMove(X)
IF NOT won
won = GetMove(O)
ENDIF
UNTIL won
EndGame(won)
END
Adding WaitForSecondPlayer()
The WaitForSecondPlayer() function causes the program to pause until the number
of players connected to the session equals 2. So the player is aware that the program
hasn't stalled, a full stop is displayed every 3 seconds. The routine's code is:
FUNCTION WaitForSecondPlayer()
PRINT "Waiting for second player ";
WHILE NumberOfPlayers() <> 2
PRINT ".";
WAIT 3000
ENDWHILE
ENDFUNCTION
Adding NumberOfPlayers()
The NumberOfPlayers() routine returns the number of entries in the checklist after
executing a PERFORM CHECKLIST FOR NET PLAYERS statement. The code
for NumberOfPlayers() is:
FUNCTION NumberOfPlayers()
PERFORM CHECKLIST FOR NET PLAYERS
result = CHECKLIST QUANTITY()
ENDFUNCTION result
We get round this problem by assigning the me and you variables different values
on the different machines. The value assigned depends on whether the player is
using the host machine; in which case, me.value is set to 1, or the client machine
where me.value is set to 2. The you variable is then set to the opposite of me. These
values are then used to decide on which sprite is displayed when the player makes
a move.
Modifying GetMyMove()
As before GetMyMove() needs to get the player's move from the mouse click,
display a symbol and update the board array. But now it must also transmit the
move chosen to the other machine using the line:
FUNCTION GetMyMove()
REPEAT
move = GetSquare()
row = move /100
col = move mod 100
result = board(row,col)
UNTIL result = 0
SPRITE nextsprite,col*33+203,row*33+203,me.imgID
board(row,col) = me.value
INC nextsprite
REM *** Send details of move to other player ***
SEND NET MESSAGE INTEGER 2,move
ENDFUNCTION
Modifying GetOpponentsMove()
The changes to the GetOpponentsMove() function are more fundamental since the
opponent's move will be made on the other machine. The routine needs to receive
details of that move over the network and then use these details to add a symbol and
update board. This gives us the code:
FUNCTION GetOpponentsMove()
REM *** Read move from network ***
REPEAT
GET NET MESSAGE
UNTIL NET MESSAGE EXISTS()
move = NET MESSAGE INTEGER()
REM *** Update board ***
row = move/100
col = move mod 100
board(row,col)= you.value
REM *** Add sprite ***
Modifying EndGame()
The only change to EndGame() is that we should free the network connection before
finishing, so we should add the line
Activity 51.21
Make the changes suggested and test out your game over a network.
A Complete Listing
A complete listing of the network version of the game is given in LISTING-51.8.
LISTING-51.8
#INCLUDE "NetConnect.dba"
Gomuku - the Networked
Version REM *********************************
REM *** Constants ***
REM *********************************
REM *** Numeric ***
#CONSTANT X 1
#CONSTANT O 2
REM *** Images ***
#CONSTANT boardimg 3
#CONSTANT ximg 1
#CONSTANT oimg 2
REM *** Sprites ***
#CONSTANT boardspr 1
REM *********************************
REM *** Types ***
REM *********************************
TYPE PlayerType
value
imgID
ENDTYPE
REM *********************************
REM *** GLOBALS ***
REM *********************************
GLOBAL DIM board(19,19)
GLOBAL nextsprite = 2
GLOBAL totalO
GLOBAL totalX
GLOBAL me AS PlayerType
GLOBAL you AS PlayerType
GLOBAL move
REM ******************************
REM *** Level 1 ***
REM ******************************
FUNCTION WaitForSecondPlayer()
PRINT "Waiting for second player ";
WHILE NumberOfPlayers() <> 2
PRINT ".";
WAIT 3000
ENDWHILE
ENDFUNCTION
FUNCTION SetUpPlayerDetails(r)
me.value = r
me.imgID = r
you.value = 3-r
you.imgID = 3-r
ENDFUNCTION
FUNCTION SetUpScreen()
SET DISPLAY MODE 1280,1024,32
COLOR BACKDROP RGB(100,50,50)
BACKDROP ON
ENDFUNCTION
FUNCTION SetUpBoard()
LOAD IMAGE "board2.bmp",boardimg
LOAD IMAGE "x.bmp",ximg
LOAD IMAGE "o.bmp",oimg
SPRITE boardspr,200,200,boardimg
ENDFUNCTION
FUNCTION GetMove(player)
IF me.value = player
GetMyMove()
ELSE
GetOpponentsMove()
ENDIF
result = CheckForWin(player)
ENDFUNCTION result
FUNCTION EndGame(won)
REM *** Find out who won ***
IF won = -1
winner$ = "X"
ELSE
winner$ = "O"
ENDIF
REM *** Display message for 5 seconds ***
SET TEXT SIZE 40
continued on next page
REM ******************************
REM *** Level 2 ***
REM ******************************
FUNCTION NumberOfPlayers()
PERFORM CHECKLIST FOR NET PLAYERS
result = CHECKLIST QUANTITY()
ENDFUNCTION result
FUNCTION GetMyMove()
REPEAT
move = GetSquare()
row = move /100
col = move mod 100
result = board(row,col)
UNTIL result = 0
SPRITE nextsprite,col*33+203,row*33+203,me.imgID
board(row,col) = me.value
INC nextsprite
REM *** Send details of move to other player ***
SEND NET MESSAGE INTEGER 2,move
ENDFUNCTION
FUNCTION GetOpponentsMove()
REM *** Read move from network ***
REPEAT
GET NET MESSAGE
UNTIL NET MESSAGE EXISTS()
move = NET MESSAGE INTEGER()
REM *** Update board ***
row = move/100
col = move mod 100
board(row,col)= you.value
REM *** Add sprite ***
SPRITE nextsprite,col*33+203,row*33+203,you.imgid
REM *** Another sprite used ***
INC nextsprite
ENDFUNCTION
FUNCTION CheckForWin(player)
result = 0
result = HorizontalCheck(player)
IF result = 0
result = VerticalCheck(player)
ENDIF
IF result = 0
result = BLTRDiagonalCheck(player)
ENDIF
IF result = 0
result = TLBRDiagonalCheck(player)
ENDIF
ENDFUNCTION result
FUNCTION HorizontalCheck(player)
REM *** Calc row and col of last move ***
row = move / 100
col = move mod 100
REM *** Upper and lower limit of search area ***
start = col - 4
IF start < 0
start = 0
ENDIF
finish = col + 4
IF finish > 18
finish = 18
ENDIF
REM *** search ***
total = 0
FOR col = start TO finish
IF board(row,col)= player
INC total
IF total = 5
EXIT
ENDIF
ELSE
total = 0
ENDIF
NEXT col
IF total = 5
result = 1
ELSE
result = 0
ENDIF
ENDFUNCTION result
FUNCTION VerticalCheck(player)
REM *** Calc row and col of last move ***
row = move / 100
col = move mod 100
REM *** Upper and lower limit of search area ***
start = row - 4
IF start < 0
start = 0
ENDIF
finish = row + 4
IF finish > 18
finish = 18
ENDIF
REM *** search ***
total = 0
FOR row = start TO finish
IF board(row,col)= player
INC total
continued on next page
FUNCTION BLTRDiagonalCheck(player)
REM *** Calc row and col of last move ***
row = move / 100
col = move mod 100
REM *** Upper and lower limit of search area (row)***
srow = row
scol = col
FOR p = 1 TO 4
IF srow = 18 OR scol = 0
EXIT
ENDIF
INC srow
DEC scol
NEXT p
frow = row
fcol = col
FOR p = 1 TO 4
IF frow = 0 OR fcol = 18
EXIT
ENDIF
DEC frow
INC fcol
NEXT p
range = fcol-scol+1
FOR p = 1 TO range
IF board(srow,scol)= player
INC total
IF total = 5
EXIT
ENDIF
ELSE
total = 0
ENDIF
DEC srow
INC scol
NEXT p
REM *** Determine result ***
IF total = 5
result = 1
ELSE
result = 0
ENDIF
ENDFUNCTION result
FUNCTION TLBRDiagonalCheck(player)
REM *** Calc row and col of last move ***
row = move / 100
col = move mod 100
REM *** Upper and lower limit of search area ***
srow = row
scol = col
continued on next page
FUNCTION Minimum(a,b)
IF a < b
result = a
ELSE
result = b
ENDIF
ENDFUNCTION result
REM ******************************
REM *** Level 4 ***
REM ******************************
FUNCTION InRange(no,min,max)
IF no >= min AND no <= max
result = 1
ELSE
result = 0
ENDIF
ENDFUNCTION result
Suggested Enhancements
As usual, there is plenty of scope for enhancing the program. An obvious addition
would be a message indicating who's turn it is. Perhaps the number of moves made,
or the time elapsed could be displayed. Or what about being able to have the program
replay all the moves in a game?
SET WINDOW ON
Activity 51.3 SET WINDOW SIZE 500,900
SET WINDOW POSITION 501,0
No solution required. SET WINDOW TITLE "CLIENT"
REM *** Get TCP/IP connection number ***
TCPIP = GetTCPIPNumber()
Activity 51.4 PRINT "Connections found"
REM *** Select TCP/IP connection ***
No solution required. SET NET CONNECTION TCPIP,"127.0.0.1"
REM *** List hosted programs ***
ListNetSessions()
JOIN NET GAME 1,"Rog"
Activity 51.5 ListPlayersDetails()
REM *** End program ***
No solution required.
WAIT KEY
END
Activity 51.6
host02.dbpro's main section should be:
REM *** Get TCP/IP connection number ***
TCPIP = GetTCPIPNumber() REM *** Use manual screen updating ***
PRINT "Connections found" SET WINDOW ON
REM *** Select TCP/IP connection *** SET WINDOW SIZE 500,900
SET NET CONNECTION TCPIP,"192.168.0.1" SET WINDOW POSITION 0,0
REM *** List hosted programs *** SET WINDOW TITLE "Host"
ListNetSessions() SYNC ON
JOIN NET GAME 1,"Rog" REM *** Get TCP/IP connection number ***
ListPlayersDetails() PRINT "Checking connections available ..."
REM *** End program *** SYNC:SYNC
WAIT KEY TCPIP = GetTCPIPNumber()
END PRINT "Connections found"
SYNC
FUNCTION GetTCPIPNumber() REM *** Set up connection ***
result = 0 PRINT "Creating Connection "
PERFORM CHECKLIST FOR NET CONNECTIONS SYNC
FOR c = 1 to CHECKLIST QUANTITY() SET NET CONNECTION TCPIP,"127.0.0.1"
IF LEFT$(CHECKLIST STRING$(c),8) = PRINT "Connection created"
Ä
"Internet" SYNC
result = c CREATE NET GAME "Passing Messages","John",
ENDIF Ä2,1
NEXT c PRINT "Program hosted"
REM *** If no connection found, stop *** SYNC
IF result = 0 REM *** End program ***
PRINT "No connection available. WAIT KEY
Ä
Press any key." END
WAIT KEY
END
ENDIF Activity 51.8
ENDFUNCTION result
No solution required.
FUNCTION ListNetSessions()
PRINT "Sessions available : "
PERFORM CHECKLIST FOR NET SESSIONS Activity 51.9
FOR c = 1 to CHECKLIST QUANTITY()
PRINT c,". ",CHECKLIST STRING$(c) No solution required.
NEXT c
PRINT "List complete"
ENDFUNCTION Activity 51.10
FUNCTION ListPlayersDetails() #INCLUDE "NetConnect.dba"
PRINT "Players in session " REM *** Use a window ***
PERFORM CHECKLIST FOR NET PLAYERS SET WINDOW ON
FOR c = 1 to CHECKLIST QUANTITY() SET WINDOW SIZE 500,900
PRINT c," ",CHECKLIST VALUE A(c), SET WINDOW POSITION 0,0
Ä
" ",CHECKLIST VALUE B(c), SET WINDOW TITLE "Host"
Ä
" ",CHECKLIST VALUE C(c), r = EstablishConnection()
Ä
LOOP
," has left the session" REM *** End program ***
MaintainPlayerList() FREE NET GAME
DisplayPlayerList() END
ENDIF
LOOP
REM *** End program *** Activity 51.17
END
The complete program is coded as:
The new routine is:
FUNCTION FindPlayerID(id)
FOR c = 1 TO noofentries
FUNCTION EndGame(won)
PRINT "EndGame"
ENDFUNCTION
Activity 51.19
The only test stub required is for CheckForWin()
Activity 51.20
No solution required.
Activity 51.21
The complete program is given in LISTING-51.8.
A common method of file transfer is File Transfer Protocol (FTP) and DarkBASIC
Pro has a set of commands specifically designed to handle this type of situation.
These instructions allow us to connect to a web site, examine and download files
from the site and even delete and add files.
Obviously, you need to be connected to the Internet (or have created your own
Intranet) to use these statements.
The Instructions
The FTP CONNECT Statement
Step 1 in accessing the files on a web site is to establish a connection between your
machine and that web site. This is done using the FTP CONNECT statement which
has the format given in FIG-52.1.
FIG-52.1
The FTP CONNECT FTP CONNECT url , name , password
Statement
In the diagram:
Many FTP sites require you to register with them and hence the need for a name
and password. However, it is also common to allow general access to many sites,
in which case the name is often "anonymous" and there is no password.
For example, we could access the FTP site, "ftp:\\ftp.darkbasic.co.uk" using the line
integer
This statement returns 1 if an attempted FTP connection is unsuccessful; otherwise
zero is returned.
string
For example, rather than simply display the word failed, as in the last example, we
could display the reason for a failure to connect using the code:
integer
This statement returns 1 if the connection is still valid, and zero if the connection
fails.
For example, we could change to the members folder within the site
ftp:\\ftp.digital-skills.co.uk using the lines:
It is possible to move one level up the folder hierarchy using the path "..". So, if the
current folder was members we could return to the original level using the line:
string
This statement will return a string detailing the current folder and path. A typical
statement would be:
FIG-52.7
FTP FIND FIRST
The FTP FIND FIRST
Statement
For example, we could move to the first file within the members folder using the
lines:
integer
string
FIG-52.11
The GET FTP FILE
GET FTP FILE SIZE ( )
SIZE Statement
integer
With the following code we could display the details of every file within the
members folder:
The program in LISTING-52.1 lists the details of the files set up by The Game
Creators' site specifically for testing FTP access.
Activity 52.1
In the diagram:
For example, we could download the welcome.msg text file from the Game Creators'
site and save it on our own machine as temp.txt using the program in LISTING-52.2.
Activity 52.2
FIG-52.14
The FTP PROCEED
Statement
FTP PROCEED
integer
The statement returns the percentage of the file's contents which has been
downloaded to your machine. When the download is complete, -1 is returned.
For example, let's assume that the Game Creators' area contains a file named
panorama.bmp (the file doesn't actually exist), then we could download it using the
following code:
FIG-52.16
FTP TERMINATE
The FTP TERMINATE
Statement
For example, we could allow the user to halt a download by pressing any key using
the lines
In the diagram:
The code below demonstrates how a file (myfile.dat) might be uploaded to an FTP
site:
Summary
l FTP stands for File Transfer Protocol.
l An FTP web site is designed to hold files and allow those with the appropriate
rights to store and retrieve files from that site.
l Use GET FTP FAILURE to check if an attempted connection to an FTP site has
failed.
l Use GET FTP ERROR$ to access a string specifying why an attempted FTP
connection has failed.
l Use GET FTP STATUS to check that an FTP connection continues to operate
correctly.
l Use GET FTP DIR$ to discover which directory within an FTP site is currently
being accessed.
l Use FTP FIND FIRST to select details of the first entry in the directory being
accessed.
l Use FTP FIND NEXT to select details of the next entry in the directory being
accessed.
l Use GET FTP FILE TYPE to discover the type of the currently selected entry.
l Use GET FTP FILE NAME to get the name of the currently selected entry.
l Use GET FTP FILE SIZE to get the size of the currently selected entry.
l Use FTP DISCONNECT to disconnect from the FTP site being accessed.
l Use FTP GET FILE to download the contents of a file from the FTP site.
l Use FTP PROCEED to get a file's next block of data from an FTP site. This is
used when large files are being downloaded.
l Use GET FTP PROGRESS to discover what percentage of a large file has been
downloaded so far.
l Use FTP DELETE FILE to delete a file from the FTP site.
Obviously, this is not the place to start teaching you another programming language,
but if you already know an appropriate language, then expanding DarkBASIC Pro
to meet your own specific needs is a very powerful feature.
Once created, a library can be linked to any new application being written and the
function within that library can then be used by the application. One way to create
such a library of functions is in a Dynamic Link Library or DLL.
In fact, all the commands in DarkBASIC Pro are held in a series of DLLs. By adding
our own DDLs we can expand the statements available to us in DarkBASIC Pro.
Creating a DLL
As we stated earlier, it is possible to create a DLL library using many different
programming languages, but here we are going to make use of Visual C++ .NET.
Obviously, if you know C or C++ what follows will be much more meaningful, but
for others, a step-by-step guide of how to add your own instructions should be useful
in the future.
At last we have created the project we require. By double clicking on the source
code file listed in the folder-structure on the left-hand side of the screen, we can
bring the file's contents into the edit window (see FIG-53.3). Notice that a few lines
have been created automatically by Visual Studio.
Activity 53.1
1
Click on
New Project
2
Select
Win32 Project
3
Enter a name 4
for your DLL Choose the folder
in which the DLL is to
be saved
5
Click on OK
FIG-53.2 1
Select
Specifying the DLL Application Settings
Option 2
Select DLL
3
Click on Finish
FIG-53.3
1
The DLL Source File Double click on
The DLL name
2
This prewritten
code will appear in
the edit area
Routines which are to be used in other applications need to begin with the rather
strange phrase:
__declspec(dllexport)
but since this is rather awkward to type, we can create a substitute term in a #define
statement:
Now when we write our code, we can use the term DarkDLL rather than
__declspec(dllexport). Just before compilation begins, the compiler will replace all
occurrences of the term DarkDLL with the correct term, __declspec(dllexport).
Since we will not be using the additional features of C++, it will be easier if we tell
the compiler to treat our code as C rather than C++. To do this we need to add the
line
extern "C"
braces are the symbols and then enclose everything we write within a set of braces.
{}
Next we need to write a function for every command we intend to add to
DarkBASIC Pro. In this example, we are going to add POS which will return the
position of a specified character within a string and OCCURS which will return the
number of times a character occurs within a string. Both of these functions were
described in Chapter 8.
Activity 53.2
Under the existing lines of code within StringFunctions.cpp, add the #define
line followed by the code for the two functions, which must be enclosed in an
extern "C" block.
Click on Build|Build Solution and make sure the code compiles correctly.
FIG-53.4
Adding a Resource
1
Right click on
StringFunctions
2
Choose Add... 3
...then click on
Add Resource
Clicking on Add Resource will produce a dialog box in which we select the String
Table option (see FIG-53.5).
FIG-53.5
Selecting a String Table
2
Click on New
1
Select String Table
FIG-53.6
An Entry in the String
Table 5
1 Click in the
A .rc resource file Caption area of the first line
has been added to the 3 to create an edit box
4
project... 2 A String Table
We need to enter details
...within this is the entry contains 3 fields:
in the Caption area
String Table ID, Value and Caption
Now we're ready to attempt the caption for our first entry in the string table. Since
the first function is called pos and we want to use the same term in DarkBASIC Pro,
then our statement name will be
POS
POS[
Next we have a separator, %, the return type, and the parameter list. The function
returns an integer (L) and takes a string (S) and character (S) as parameters, so the
caption now becomes:
POS[%LSS
POS[%LSS%pos
POS[%LSS%pos%string,string
Activity 53.3
Write down the caption required for the occurs() function. Enter the captions
for both functions into the string table and close the table.
DarkBASIC Pro contains a folder named Compiler, and within this is a sub-folder
named plugins-user. We need to copy StringFunctions.dll into this folder. Once this
FIG-53.7 is done, the new statements, POS and OCCURS will be recognised by DarkBASIC
Pro (see FIG-53.7).
Copying the DLL
A$ = "ABCDEFBBBFRGB"
B$ = "B"
PRINT B$," appears ",OCCURS(A$,B$)," times"
StringFunctions.dll PRINT B$," first appears at position ", POS(A$,B$)
WAIT KEY
END
StringFunctions.dll
Building the code within C++ will This file must be copied into Now the new statements can be used
create a file named StringFunctions.dll Plugins-user in the DarkBASIC Pro's within DarkBASIC Pro.
in the Debug sub-folder. Compiler folder
Carry out the steps explained above and test out the OCCURS and POS
statements in a short DarkBASIC Pro program.
Adding Help
Our new commands aren't fully integrated into DarkBASIC Pro because, even
though they function correctly, they are not highlighted in blue and don't bring up
a help window when you move the cursor over the statement and press F1. But,
with a bit more effort, we can have all that happen too.
The blue highlight is created by having a command listed in one of several special
.ini files. These files are in DarkBASIC Pro's Keywords folder within the Editor
(see FIG-53.8).
FIG-53.8
The .ini Files
Only statements listed in one of these files will appear with a blue highlight in the
edit window, so if we want our commands to be highlighted, they must either be
added to one of the existing .ini files, or placed in a new .ini file.
In this case, we'll create a new .ini file - that way it will be easier to remove all our
changes should that be required.
Activity 53.5
OCCURS=commands\extras\OCCURS.htm=(searched,searchedfor)
POS=commands\extras\POS.htm=(searched,searchedfor)
Load up the DarkBASIC Pro program you created in Activity 53.4. Are the
terms OCCURS and POS now highlighted in blue?
Although we've created the highlighting, we still need to create a help page for each
statement. In fact, the .ini file actually lists details of the help file name and where
it is to be found. For example, in the line
OCCURS=commands\extras\OCCURS.htm=(searched,searchedfor)
the term commands\extras\OCCURS.htm tells DarkBASIC Pro that the help page
for the OCCURS statement is in a file named OCCURS.htm which is to be found
Activity 53.6
Now we need to create each of the help files. These are just HTML files giving
details of a command. The code for OCCURS.htm is shown below:
Activity 53.7
Create both OCCURS.htm and POS.htm, containing the code given and save
them in the extras folder.
It is usual for all the instructions that belong to a specific group (for example, camera
statements, lighting statements, etc.) to have an HTML file listing each of the
commands in that group with links to their individual help pages. In fact, our two
new pages already have links to the extras command list page - even though we
haven't created it yet!
FIG-53.11
A Group Help File
Activity 53.8
But although the code will compile, the value returned bears no resemblance to 12.3.
To make the whole thing work as expected, we need to start by changing the return
type to DWORD
return *(DWORD*)&result;
RETURNREAL[%F%returnfloat
Notice on this occasion the term used in DarkBASIC Pro is RETURNREAL, while
the function is actually called returnfloat. Also, since there are no parameters, the
final part of the entry is missing.
#include "globstruct.h"
You'll find this file in some examples embedded in DarkBASIC Pro in the folder
The file must be copied into the folder containing your DLL source code.
Unfortunately, globstruct.h makes references to other header files from the DirectX
development kit. If you don't have this, you won't be able to compile your program.
However, assuming only simple functions are required in your DLL we can doctor
Activity 53.9
Find globstruct.h and copy it into the DLL source code folder.
char pEXEUnpackDirectory[_MAX_PATH];
to
char pEXEUnpackDirectory[260];
Back in the code for the DLL, we need to create a global variable immediately after
the new #include statement:
Activity 53.10
#include "globstruct.h"
GlobStruct* g_pGlob = NULL;
immediately after
#include "stdafx.h"
A function that returns a string has to be written as returning a DWORD type. But
it also needs to use a phantom parameter of the same type:
Within the routine we need to treat the parameter as a pointer and delete any space
it is referencing with the lines:
The string which is going to contain the result needs to be set up with lines such as:
LPSTR strresult=NULL;
g_pGlob->CreateDeleteString((DWORD*)&strresult,10);
The value at the end of the second line is the size of the string, taking into account
that strings in C++ must be assigned an extra character position for the terminating
null character.
Now, as long as we cast strresult to LPSTR we can treat it as a normal C++ string.
For example, we could store the character sequence ABCD in the string using the
line:
strcpy((LPSTR)strresult,"ABCD");
To return the string from the function we need to end with a line such as
return (DWORD)strresult;
The Caption in the string table holds another surprise. We'll start by using a
DarkBASIC Pro type name for the new instruction
ABCD$[
ABCD$[%S
but the phantom parameter is ignored, so we continue directly to the function name:
ABCD$[%S%returnstring
Because there are no parameters, the final comment section of the string can be
omitted or the term void used.
Activity 53.11
Add returnstring() to your DLL file along with an entry in the string table.
After testing, remove the returnstring() function and its string table entry from
your C++ project.
LISTING-53.1 DarkDLL DWORD insertstr(DWORD olds, DWORD s1, DWORD s2, int post)
{
C++ Code for the New if(olds)
Statements g_pGlob->CreateDeleteString ( (DWORD*)&olds, 0 );
if (post < 1)
post = 1;
if (post > strlen((LPSTR)s1)+1)
post = strlen((LPSTR)s1)+1;
LPSTR strresult=NULL;
g_pGlob->CreateDeleteString((DWORD*)&strresult,
Ästrlen((LPSTR)s1)+strlen((LPSTR)s2)+1 );
strncpy(strresult,(LPSTR)s1,post-1);
strresult[post-1]='\0';
strcat(strresult,(LPSTR)s2);
strcat(strresult,&((LPSTR)s1)[post-1]);
return (DWORD)strresult;
}
Activity 53.12
Add the three functions given in LISTING-53.1 to your DLL source code.
Summary
l A Dynamic Link Library (DLL) is a file containing a collection of functions.
l By including all functions within an extern "C" block, function names used by
the operating system match those given in the source code.
l Functions not included in an extern block have new names allocated by the C++
compiler.
l The mangled name of a function can be found within the DLL file generated by
the C++ compiler.
l Return values and parameters used when coding DLL functions must match
those required by DarkBASIC Pro.
l Entries in a .ini file give the name of the command, the path to the corresponding
HTML help file and a parameter list.
l An HTML file should be added to the appropriate folder for each new command.
l Elements of globstruct.h must be edited or other .h files from DirectX SDK must
be made available.
l The phantom parameter is omitted from the string table and ignored when calling
the function.
However, any functions within a DLL using real or string values must still be written
using the techniques explained in the previous section of this chapter.
#include "stdafx.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
The first, factorial(), returns the factorial of a specified value. A factorial of a value
is the result obtained by multiplying the value by every integer from 2 up to itself.
For example, factorial 5 (written in mathematics as 5!) is 2*3*4*5, or, 120.
The second function, reciprocal(), returns the value of 1 divided by the number
given. For example, the reciprocal of 5 is 1/5, or 0.2.
Activity 53.13
Create a new C++ Win32 DLL project and type in the code given in
LISTING-53.2. Check that the code compiles correctly.
In the diagram:
For example, we could load functions.dll into our current project using the line:
integer
In the diagram:
Our program should check for the DLL being loaded immediately after the LOAD
DLL statement:
In the diagram:
For example, we could execute the factorial() function in our DLL using the line
CALL DLL(1,"factorial",5)
integer
In the diagram:
The statement returns 1 if the specified function exists within the DLL, otherwise
zero is returned.
In the diagram:
Activity 53.14
Run the program and check that the two functions are executed correctly.
Summary
l DLLs not designed to create new DarkBASIC Pro instructions can also be called
from a DarkBASIC Pro program.
l Use DLL CALL EXIST to check that a function of a specified name exists within
a DLL.
At last, we've reached the end of two massive volumes covering most of
DarkBASIC Pro's statements. I hope you have found the effort worthwhile and are
inspired to start work on a game of your own.
But remember, there's a lot more you can learn from keeping in touch on The Game
Creators forums where you will find people with an amazing amount of experience
who are more than willing to help with any problems you have.
If you find any mistakes in this text or have any ideas about extra topics that might
be covered, I would genuinely like to hear from you. You can contact me at
alistair@digital-skills.co.uk. You'll also find supplementary material on the
DarkBASIC Pro section of our website at www.digital-skills.co.uk.
Activity 53.2
Activity 53.6
The code in StringFunctions.cpp should be:
No solution required.
// StringFunctions.cpp : Defines the entry
Äpoint for the DLL application.
// Activity 53.7
#include "stdafx.h" No solution required.
BOOL APIENTRY DllMain
( Activity 53.8
HANDLE hModule,
DWORD ul_reason_for_call, No solution required.
LPVOID lpReserved
)
{ Activity 53.9
return TRUE;
} No solution required.
#define DarkDLL __declspec(dllexport)
Activity 53.10
extern "C"
{ No solution required.
DarkDLL int pos(LPSTR s1,LPSTR ch)
{
int result = -1;
for(int c = 0; c < strlen(s1);c++)
Activity 53.11
if (s1[c] == ch[0]) The code in StringFunctions.cpp should now be:
{
result = c+1;
// StringFunctions.cpp : Defines the entry
}
break;
Äpoint for the DLL application.
//
return result;
}
#include "stdafx.h"
#include "globstruct.h"
DarkDLL int occurs(LPSTR s1,LPSTR ch)
GlobStruct* g_pGlob = NULL;
{
int result = 0;
for(int c = 0; c < strlen(s1);c++)
BOOL APIENTRY DllMain
if (s1[c] == ch[0])
(
result++;
HANDLE hModule,
return result;
DWORD ul_reason_for_call,
}
LPVOID lpReserved
}
)
{
return TRUE;
Activity 53.3 }
The string table caption for occurs() should be: #define DarkDLL __declspec(dllexport)
OCCURS[%LSS%occurs%string,string extern "C"
{
DarkDLL int pos(LPSTR s1,LPSTR ch)
Activity 53.4 {
int result = -1;
A very simple DarkBASIC Pro program such as for(int c = 0; c < strlen(s1);c++)
if (s1[c] == ch[0])
PRINT POS("ABCD","C") {
PRINT OCCURS("ABBBCDBBDB","B") result = c+1;
WAIT KEY break;
END }
return result;
should act as a basic test of the new statements. }
PRINT ABCD$()
WAIT KEY
END
Activity 53.12
String table entries for the new functions should be:
INSERT$[%SSSL%insert%string,string,int
DELETE$[%SSLL%deletestr%string,int,int
REPLACE$[%SSSL%replacechr%string,character,int
PRINT INSERT$("ABCD","XYZ",2)
PRINT DELETE$("ABCD",2,2)
PRINT REPLACE$("ABCD","X",3)
WAIT KEY
END
Activity 53.13
No solution required.
Activity 53.14
No solution required.
A Culling 784
C E
CALL DLL 1463
Cameras 751, 836 Edge 749
CAMERA ANGLE 844 EFFECT EXIST 1378
CAMERA POSITION 844 Elevator model 948
Castle model 822 emitter 1102
CHANGE MESH 931 extern "C" 1448
CHANGE MESH FROM MEMBLOCK 1310
CHECK LIMB LINK
CLEAR CAMERA VIEW
947
869
F
Client/server 1390
CLONE OBJECT 778 FADE OBJECT 817
Collision box 1071 File formats 963
Collision sphere 1071 FILL MATRIX 1222
COLOR AMBIENT LIGHT 889 FIRE PARTICLES 1119
COLOR BACKDROP 864 FIX OBJECT PIVOT 773
COLOR LIGHT 890 FOG 901
COLOR LIMB 922 FOG COLOR 902
COLOR OBJECT 815 FOG DISTANCE 902
COLOR PARTICLES 1107 Frame rate 750
Column vectors 1350 FREE NET GAME 1411
FREE NET PLAYER 1412 Internet File Transfers 1436
FTP 1436 INSTANCE OBJECT 779
FTP CONNECT 1436 INTERSECT OBJECT 1083
FTP DELETE FILE 1442 INVERSE MATRIX4 1370
FTP DISCONNECT 1440 IPX 1391
FTP FIND FIRST 1438 IS EQUAL MATRIX4 1367
FTP FIND NEXT 1439 IS EQUAL VECTOR3 1359
FTP GET FILE 1440 IS IDENTITY MATRIX4 1366
FTP PROCEED 1441
FTP PUT FILE
FTP SET DIR
1443
1438 J
FTP TERMINATE 1442
FX Files 1376 JOIN NET GAME 1396
G L
GET FTP DIR$ 1438 LAN 1390
GET FTP ERROR$ 1437 LENGTH VECTOR3 1355
GET FTP FAILURE 1436 LIGHT DIRECTION 900
GET FTP FILE NAME$ 1439 LIGHT EXIST 898
GET FTP FILE SIZE 1439 Light map 1035
GET FTP FILE TYPE 1439 LIGHT POSITION 900
GET FTP PROGRESS 1442 LIGHT RANGE 899
GET FTP STATUS 1437 LIGHT TYPE 899
GET GROUND HEIGHT 1218 LIGHT VISIBLE 899
GET INDEXDATA 1268 Lighting 752, 886
GET MATRIX HEIGHT 1217 Limbs 917
GET MAXIMUM PIXEL SHADER VERSION 1377 LIMB ANGLE 939
GET MAXIMUM VERTEX SHADER VERSION 1377 LIMB DIRECTION 940
GET MEMBLOCK PTR 1281 LIMB EXIST 936
GET MEMBLOCK SIZE 1286 LIMB NAME$ 945
GET NET MESSAGE 1401 LIMB OFFSET 937
GET OBJECT COLLISION 1080 LIMB POSITION 939
GET PICK DISTANCE 984 LIMB SCALE 938
GET STATIC COLLISION 1089 LIMB TEXTURE 946
GET STATIC COLLISION HIT 1087 LIMB TEXTURE NAME 946
GET TERRAIN GROUND HEIGHT 1191 LIMB VISIBLE 937
GET TERRAIN HEIGHT 1184 LINK LIMB 928
GET TERRAIN SIZE 1193 LOAD BSP 1165
GET TOTAL TERRAIN HEIGHT 1186 LOAD DLL 1463
GET VERTEXDATA 1255 LOAD EFFECT 1378
GET VERTEXDATA DIFFUSE 1258 LOAD IMAGE 800
GET VERTEXDATA INDEX COUNT 1267 LOAD MESH 914
GET VERTEXDATA NORMALS 1253 LOAD OBJECT 950, 963
GET VERTEXDATA POSITION 1248 LOAD TERRAIN 1194
GET VERTEXDATA VERTEX COUNT 1247 Local axes 747
GHOST MATRIX OFF 1232 LOCK OBJECT 874
GHOST MATRIX ON 1231 LOCK VERTEXDATA FOR LIMB 1251
GHOST OBJECT OFF 817 LOCK VERTEXDATA FOR MESH 1246
GHOST OBJECT ON 816 LOOP OBJECT 966
GHOST PARTICLES OFF 1114
M
GHOST PARTICLES ON 1113
GLUE OBJECT TO LIMB 931
Gomuku 1415
MAKE BITMAP FROM MEMBLOCK 1299
H MAKE CAMERA
MAKE FILE FROM MEMBLOCK
863
1292
MAKE FIRE PARTICLES 1119
HIDE LIGHT 887 MAKE IMAGE FROM MEMBLOCK 1302
HIDE LIMB 927 MAKE LIGHT 889
HIDE OBJECT 776 MAKE MATRIX 1213
HIDE OBJECT BOUNDS 1072 MAKE MATRIX4 1365
HIDE PARTICLES 1103 MAKE MEMBLOCK 1281
Host 1392 MAKE MEMBLOCK FROM BITMAP 1297
MAKE MEMBLOCK FROM FILE 1294
I MAKE MEMBLOCK FROM IMAGE
MAKE MEMBLOCK FROM MESH
1302
1306
MAKE MEMBLOCK FROM SOUND 1303
Importing 3D Objects 962 MAKE MESH FROM MEMBLOCK 1309
MAKE MESH FROM OBJECT 912 OBJECT COLLISION CENTER 1075
MAKE OBJECT 914 OBJECT COLLISION RADIUS 1075
MAKE OBJECT BOX 757 OBJECT EXIST 779
MAKE OBJECT COLLISION BOX 1077 OBJECT FRAME 972
MAKE OBJECT CONE 760 OBJECT HIT 1069
MAKE OBJECT CYLINDER 759 OBJECT IN SCREEN 987
MAKE OBJECT CUBE 756 OBJECT INTERPOLATION 972
MAKE OBJECT FROM LIMB 919 OBJECT LOOPING 971
MAKE OBJECT PLAIN 760 OBJECT PLAYING 971
MAKE OBJECT SPHERE 758 OBJECT POSITION 780
MAKE OBJECT TERRAIN 1186 OBJECT SCREEN 982
MAKE OBJECT TRIANGLE 761 OBJECT SIZE 781, 973
MAKE PARTICLES 1102 OBJECT SPEED 972
MAKE SNOW PARTICLES 1118 OBJECT VISIBLE 780
MAKE SOUND FROM MEMBLOCK 1305 ODE 1318
MAKE STATIC COLLISION BOX 1087 ODE ADD FORCE 1338
MAKE TERRAIN 1180 ODE COLLISION GET MESSAGE 1336
MAKE VECTOR3 1351 ODE COLLISION MESSAGE EXISTS 1336
Manipulating Vetices 1246 ODE CREATE DYNAMIC BOX 1318
Map 1164 ODE CREATE DYNAMIC CYLINDER 1322
Matrices 1212 ODE CREATE DYNAMIC TRIANGLE MESH 1324
MATRIX EXIST 1237 ODE CREATE STATIC BOX 1321
MATRIX POSITION 1230 ODE CREATE STATIC TRIANGLE MESH 1325
MATRIX TILE COUNT 1228 ODE DESTROY OBJECT 1334
MATRIX TILES EXIST 1228 ODE END 1319
MATRIX WIREFRAME STATE 1220 ODE GET BODY HEIGHT 1335
MAXIMIZE VECTOR3 1359 ODE GET BODY LINEAR VELOCITY 1335
MEMBLOCK 1285 ODE GET OBJECT 1336
MEMBLOCK EXIST 1286 ODE GET OBJECT ANGULAR VELOCITY 1338
Memory Blocks ODE GET OBJECT VELOCITY 1337
Media 1297 ODE MAKE DYNAMIC SPHERE 1322
Strings 1288 ODE SET ANGULAR VELOCITY 1331
Merging 3D primitives 788 ODE SET BODY MASS 1332
MESH EXIST 915 ODE SET BODY ROTATION 1332
Meshes 912 ODE SET CONTACT FDIR1 1328
MINIMIZE VECTOR3 1360 ODE SET LINEAR VELOCITY 1328
Mipmaps 799 ODE SET WORLD CFM 1327
MOVE CAMERA 837 ODE SET WORLD ERP 1326
MOVE OBJECT 764 ODE SET WORLD GRAVITY 1320
MOVE OBJECT distance 772 ODE SET WORLD STEP 1325
Multiple cameras 863 ODE START 1319
MULTIPLY MATRIX4 1369 ODE UPDATE 1319
MULTIPLY VECTOR3 1354 OFFSET LIMB 920
N P
NET BUFFER SIZE 1408 Packets 1392
NET GAME EXISTS 1413 Particles 1102
NET GAME LOST 1413 PARTICLES EXIST 1114
NET GAME NOW HOSTING 1411 PARTICLES POSITION 1115
NET MESSAGE 1402 Peer-to-peer 1390
NET MESSAGE EXISTS 1402 PERFORM CHECKLIST FOR EFFECT 1379
NET MESSAGE PLAYER FROM 1403 PERFORM CSG DIFFERENCE 790
NET MESSAGE PLAYER TO 1403 PERFORM CSG INTERSECTION 790
NET MESSAGE TYPE 1406 PERFORM CSG UNION 788
NET PLAYER CREATED 1409 PERFORM CHECKLIST FOR NET CONNECTIONS 1391
NET PLAYER DESTROYED 1409 PERFORM CHECKLIST FOR NET PLAYERS 1397
Network 1390 PERFORM CHECKLIST FOR NET SESSIONS 1395
Networked games 1390 PERFORM CHECKLIST FOR OBJECT LIMBS 944
NORMALIZE VECTOR3 1358 PICK SCREEN 986
Normals 752 PICK OBJECT 983
surface 752 PICK VECTOR 985
vertex 753 PITCH CAMERA 842
PITCH OBJECT 770
O
Pixel shader 1376
Planes 745
PLAY ANIMATION TO IMAGE 805
PLAY OBJECT 965
OBJECT ANGLE 781
POINT CAMERA 838
OBJECT COLLISION 1070
POINT LIGHT 893
POINT OBJECT 771 SET CARTOON SHADING ON 1054
Points 746 SET CUBE MAPPING ON 1042
Pointers 1280 SET CURRENT CAMERA 865
Polygon 749 SET DETAIL MAPPING ON 811
POSITION CAMERA 836 SET DIRECTIONAL LIGHT 892
POSITION LIGHT 891 SET EFFECT CONSTANT 1383
POSITION MATRIX 1229 SET EFFECT ON 1380
POSITION OBJECT 762 SET EFFECT TECHNIQUE 1383
POSITION PARTICLE EMISSIONS 1105 SET EFFECT TRANSPOSE 1384
POSITION PARTICLES 1104 SET GLOBAL COLLISION 1071
POSITION TERRAIN 1182 SET GLOBAL OBJECT CREATION 785
PREPARE MATRIX TEXTURE 1220 SET GLOBAL SHADOW COLOR 1050
PROCESS BSP COLLISION 1171 SET GLOBAL SHADOW SHADES 1050
SET GLOBAL SHADOWS 1048
R
SET IDENTITY MATRIX4 1366
SET INDEXDATA 1270
SET LIGHT MAPPING ON 1035
SET LIGHT RANGE 891
RANDOMIZE MATRIX 1214
SET LIGHT TO OBJECT ORIENTATION 897
READ MEMBLOCK 1292
SET LIGHT TO OBJECT POSITION 895
REMOVE LIMB 928
SET LIMB EFFECT 1381
ROLL CAMERA 843
SET LIMB SMOOTHING 934
ROLL OBJECT 771
SET MATRIX 1235
Roman candle 1121
SET MATRIX HEIGHT 1215
ROTATE CAMERA 838
SET MATRIX NORMAL 1234
ROTATE LIGHT 895
SET MATRIX PRIORITY 1232
ROTATE LIMB 920
SET MATRIX TILE 1223
ROTATE MATRIX4 1371
SET MATRIX WIREFRAME 1219
ROTATE OBJECT 768
SET NET CONNECTION 1392
ROTATE PARTICLES 1106
SET OBJECT AMBIENCE 1029
Rotation 748
SET OBJECT COLLISION 1070
Absolute 769
SET OBJECT COLLISION TO BOXES 1076
Relative 769
SET OBJECT COLLISION TO POLYGON 1076
SET OBJECT COLLISION TO SPHERE 1074
S SET OBJECT CULL
SET OBJECT DIFFUSE
784
1030
SET OBJECT EMISSIVE 1031
SAVE MESH 913 SET OBJECT EFFECT 1379
SAVE OBJECT 949 SET OBJECT FILTER 813
SAVE TERRAIN 1193 SET OBJECT FOG 903
SCALE LIMB 922 SET OBJECT FRAME 968
SCALE LIMB TEXTURE 925 SET OBJECT INTERPOLATION 969
SCALE MATRIX4 1370 SET OBJECT LIGHT 1034
SCALE OBJECT 775 SET OBJECT ROTATION 769
SCALE OBJECT TEXTURE 802 SET OBJECT RADIUS 1074
SCALE VECTOR3 1354 SET OBJECT SPECULAR 1030
SCROLL LIMB TEXTURE 927 SET OBJECT SPECULAR POWER 1031
SCROLL OBJECT TEXTURE 808 SET OBJECT SPEED 967
Seamless tiling 804 SET OBJECT TEXTURE 807
SEND NET MESSAGE 1401, 1404 SET OBJECT TO CAMERA ORIENTATION 873
SET ALPHA MAPPING ON 1044 SET OBJECT TRANSPARENCY 810
SET AMBIENT LIGHT 888 SET OBJECT WIREFRAME 783
SET BLEND MAPPING ON 1041 SET PARTICLE CHAOS 1110
SET BUMP MAPPING ON 1038 SET PARTICLE EMISSIONS 1108
SET BSP CAMERA 1173 SET PARTICLE FLOOR 1112
SET BSP CAMERA COLLISION 1167 SET PARTICLE GRAVITY 1110
SET BSP CAMERA COLLISION RADIUS 1169 SET PARTICLE LIFE 1113
SET BSP COLLISION HEIGHT ADJUSTMENT 1170 SET PARTICLE SPEED 1111
SET BSP COLLISION OFF 1171 SET PARTICLE VELOCITY 1109
SET BSP COLLISION THRESHOLD 1171 SET PIXEL SHADER OFF 1386
SET BSP MULTITEXTURING 1173 SET PIXEL SHADER ON 1386
SET BSP OBJECT COLLISION 1167 SET PIXEL SHADER TEXTURE 1386
SET BSP OBJECT COLLISION RADIUS 1169 SET POINT LIGHT 893
SET CAMERA ASPECT 846 SET RAINBOW SHADING ON 1056
SET CAMERA FOV 847 SET REFLECTION SHADING ON 1057
SET CAMERA RANGE 848 SET SHADING OFF 1058
SET CAMERA ROTATION 840 SET SHADOW POSITION 1051
SET CAMERA TO FOLLOW 853 SET SHADOW SHADING ON 1045
SET CAMERA TO IMAGE 871 SET SHADOW SHADING OFF 1048
SET CAMERA TO OBJECT ORIENTATION 873 SET SPHERE MAPPING ON 1039
SET CAMERA VIEW 845 SET SPOT LIGHT 892
SET TERRAIN HEIGHTMAP
SET TERRAIN LIGHT
1187
1190
V
SET TERRAIN SCALE 1187
SET TERRAIN SPLIT 1191 Vertex 749
SET TERRAIN TEXTURE 1188 Vertex buffer 785
SET TERRAIN TILING 1189 Vertex data buffer's structure 1266
SET TEXTURE TRIM 1226 Vertex shader 1376
SET VECTOR3 1352 Video texture 805
SET VECTOR3 TO CAMERA POSITION 875
SET VECTOR3 TO CAMERA ROTATION
SET VECTOR3 TO PARTICLES POSITION
876
1116 W
SET VECTOR3 TO PARTICLES ROTATION 1116
SET VERTEX SHADER STREAM 1386 WAN 1390
SET VERTEX SHADER STREAMCOUNT 1386 Wireframe 749
SET VERTEX SHADER VECTOR 1386 World 1164
SET VERTEXDATA DIFFUSE 1257 World axes 744
SET VERTEXDATA NORMALS 1254 World units 747
Sky spheres 828 WRITE MEMBLOCK 1284
SET VERTEXDATA POSITION 1250 WRITE MEMBLOCK 1290
SET VERTEXDATA UV 1256
Shaders 1376
Shadows
SHIFT MATRIX
1045
1227
X
SHOW LIGHT 888
SHOW LIMB 928 XROTATE CAMERA 840
SHOW OBJECT 776 XROTATE OBJECT 766
SHOW OBJECT BOUNDS 1072
SHOW PARTICLES
Skybox
1104
1197 Y
SNOW PARTICLES 1118
Solitaire 994 YROTATE CAMERA 841
Spaceship 1122 YROTATE OBJECT 767
Spot lighting 886
Z
SQUARED LENGTH VECTOR3 1356
Standard DLLs 1462
Static collision 1068
STATIC LINE OF SIGHT 1093
ZROTATE CAMERA 841
STATIC LINE OF SIGHT coordinates 1095
STOP OBJECT 968 ZROTATE OBJECT 767
String table 1449
Subdivisions 1212
SUBTRACT MATRIX4 1368
SUBTRACT VECTOR3 1357
Surface reflection 1028
Switching between cameras 866
T
TCP/IP 1391
Terrain 1180
TERRAIN POSITION 1183
Textures 750
TEXTURE LIMB 923
TEXTURE OBJECT 798
TEXTURE TERRAIN 1183
Tiling 801
TOTAL OBJECT FRAMES 966
TURN CAMERA 842
TURN OBJECT 770
TRANSLATE MATRIX4 1371
TRANSPOSE MATRIX4 1372
U
UNGLUE OBJECT 934
UNLOCK VERTEXDATA 1250
UPDATE MATRIX 1214
Also Available
from our website www.digital-skills.co.uk
Background Concepts
Milkshape Basic Controls
Principles of 3D Construction
Vertices, Edges and Faces
3D Primitives
Manipulating Vertices
Reshaping Meshes
Extrusion
Using Milkshapes Additional Tools
Groups
Creating Models of Real World Objects
Texturing Your Model
Animation
Exporting Your Model to DBPro