Simple 3D FPS Movement Demo (moonlit camera in woods)— Revised

I’ve done some slight revisions to the 3D FPS Movement Demo project I previously posted. Instead of using the OrbitViewer class from @John ’s “Cameras” Codea dependency, I switched to the FirstPersonViewer class in the same dependency. I the touch screen camera rotation in the FirstPersonViewer class combined with a simple cluster of keyboard inputs to move the camera in 3D space accomplishes more of what I was looking for in terms of 3D First Person Shooter type movement in my Codea projects.

I’ve posted the revised code below and have additionally attached a Zip file of the project’s code and attached the project’s texture assets. As I mentioned in my original Simple 3D FPS Movement Demo post, this forum does not allow me to attach the .obj model so you will either need to download the free model from:

https://www.turbosquid.com/3d-models/old-house-max-free/1117878 (choose .obj format to download) or substitute your own model as an asset (if you do so, remember to change asset.documents statements in the code to point to your assets).

-- 3D FPS demoR (Revised)

function setup()
    viewer.mode = FULLSCREEN
    
    -- need to enable keyboard with below command to get input (even if use external keyboard)
    showKeyboard()
    
    -- Create a new craft scene
    scene = craft.scene()
    
    -- skybox (developers special function for-- really a box around scene painted inside with a cross map texture)
    -- *Note: grimmnight texture free download (per download site to be credited to "Hipshot" at www.zfight.com)
    scene.sky.material.envMap = craft.cubeTexture(asset.documents.grimmnight_large)
    
    -- Create a ground entity from primitives
    -- *Note: "forest-floor-terrain" free download from texturemax.com
    Ground = scene:entity()
    Ground.model = craft.model.cube(vec3(1.2,0.01,1.2))
    Ground.material = craft.material(asset.builtin.Materials.Basic)
    Ground.material.map = readImage(asset.documents["forest-floor-terrain.jpg"])
    Ground.y = 0
    
    -- import 3D model of house(.obj format) from document folder in Codea
    -- *Note: "old_house" model and "wood" texture are free downloads from TurboSquid website
    House = scene:entity()
    House.model = craft.model(asset.documents.old_house)
    House.material = craft.material(asset.builtin.Materials.Basic)
    House.material.map = readImage(asset.documents.wood_D_png)
    -- *Next line rotates the house model 180 degrees around y axis so will later line up nicely with backdrop moon in sky
    House.eulerAngles = vec3(0, 180, 0)
    House.scale = vec3(0.005,0.005,0.005)
    
    -- Link "Cameras" dependency to this project; dependency contains FirstPersonViewer class that you can add to scene camera to rotate camera view by touching the screen
    -- Note: it helps to assign FirstPersonViewer camera to a variable so that can set its properties (e.g. v = scene.camera:add(FirstPersonViewer))
    v = scene.camera:add(FirstPersonViewer)
    -- *Next line rotates the camera 180 around y axis so when program runs camera points at front of cabin with moon in the sky as a backdrop and at an arbitrary + 4 z axis units in front of the cabin 
    -- Note that v.ry defines camera rotation view while v.camera.z sets actual z coordinates of the camera in 3D space
    v.ry= 180
    v.camera.z = 4
    
    function update(dt)
        -- Update the scene (physics, transforms etc)
        scene:update(dt)
    end
    
    function draw() 
        --update then draw 3D "scene"
        update(DeltaTime)
        scene:draw() 
    end
    
    function keyboard(key)
        -- this function detects keyboard input and adds to "Cameras" dependency the ability (without rotating the camera's view) to move the first person camera (FirstPersonViewer) stepwise in 3D space (arbitrarily 0.25 units)
        -- (i/j/k/l/u/m = fwd (+z)/left (-x)/back (-z)/right (+x)/up (+y)/down (-y))
        -- control speed of movement in x,y,z space by changing constant value to add to viewer position (v) when move
        -- *Note: use original FirstPersonViewer function of touching the screen to rotate in place the camera's view     
        if key == "i" then v.camera.z = v.camera.z -0.25
        elseif key == "j" then v.camera.x = v.camera.x - .25
        elseif key == "k" then v.camera.z = v.camera.z + 0.25
        elseif key == "l" then v.camera.x = v.camera.x + .25
        elseif key == "u" then v.camera.y = v.camera.y + .25
        elseif key == "m" then v.camera.y = v.camera.y - .25
        end  
    end 
end

Have fun! :slight_smile:

@Bri_G and @binaryblues,

You might be interested in trying a slightly more interesting model using this “revised” FPS movement demo.

3D Maze:

  1. Download and unzip the “revised” FPS demo (CraftFPSMove), forrest-floor and grimmnight assets above

  2. Change the House.model in the above code from the default house.obj model to the free 3D Maze .obj model available for download from:

https://www.cgtrader.com/items/2215957/download-page

  1. Consider changing the House.material.map in the above code from the included wood texture to a stone wall texture (see attached jpeg) or download for free from:

https://freestocktextures.com/texture/sandstone-sedimentary-wall,814.html#google_vignette

(Large file—jpeg attached has been significantly compressed)

  1. Change line 31 of the code to increase the scale of this new model so appropriately scales to the size for the new model:

House.scale = vec3(0.05,0.05,0.05)

  1. Change lines 51-63’s of the codes’s constant value of movement from 0.25 to 0.0625 to allow more finely controlled 3D movement through the maze

Note: I didn’t add in any collision physics so you can cheat and walk through walls. (You can also cheat by using the “u” key to rise above the maze to see where you are).

Fyi :slight_smile:

@SugarRay - you need to modify the code section, in your post, to include the first few lines, not formatted, as I had errors thrown up which didn’t relate to the lack of function setup() and the corresponding end statements

@Simeon @dave1707 the missing function setup() seemed to exclude the end statement so the code from setup became global. Is this expected ? Fell asleep whilst copying and posting so missed this.

@Simeon @John @dave1707 - quick dumb question : is the asset addressing system nullified by a numeric character as the first character in an asset string?
Tried to use 3DMaze as a folder and file and they were not visible.

@SugarRay - loaded up as you described but no movement through maze just up and down motion of floor. Could be easier setting up x,y and rotation parameters to move. Also floor very small. Not sure if I have the same maze object as you.

Will play around with it and post back anything that may help.

@Bri_G I created a folder named 3DMaze and it shows in the project list. I had to scroll all the way to the bottom of the list, past Craft, and Examples.

@dave1707 - do you mean you tried :

    Path = asset.documents.3DMaze

and it showed up after all the alphabetic text was exhausted ?

Edit: this may be an iOS feature but I created the folder then tried to access it as above. Image shows what I get.

@Bri_G My mistake. I could do

    sprite(asset.documents.Dropbox.Maze.."spinner",WIDTH/2,HEIGHT/2)

but not

    sprite(asset.documents.Dropbox.."3DMaze.spinner",WIDTH/2,HEIGHT/2)

On the second one, I don’t get any kind of an error, the sprite just doesn’t show.

@dave1707 - yeah, not sure if I mentioned this before but it looks like parsing the path string treats numeric input as numbers and not string characters. Need to test if it is only as first character as it could be treating the period as a decimal point.

Cc: @Simeon @John can you confirm ?

@Bri_G I was able to use

    file = os.getenv("HOME").."/Documents/Dropbox.assets/3DMaze/spinner.png"

when reading the image using the file io commands.

So the 3DMaze worked there.

@SugarRay Thank you for the introduce of the 3DMaze, I will try it later.

@Bri_G I was able to get this to work. The 3DMaze requires an _ (underscore) before the 3D .

    sprite(asset.documents.Dropbox._3DMaze.spinner,WIDTH/2,HEIGHT/2)

The folder doesn’t show up in the Codea app, but it does in the Files app. Even though it doesn’t show up, you still have access to it.

@dave1707 - thanks for the feedback, well figured out. It’s not ideal but a nice workaround, but - it does need publicity and adding to the reference for path addressing. Thanks.

Thanks, @Bri_G and @dave1707 for all of your efforts in getting this to work out for others— I didn’t think it would be so difficult for others to load. Please let me know if there are things I can do in the future to make my projects easier for others to utilize right from the post. I realize that not being able to include the .obj model may make it more complicated for others to set up. I had added the 3D maze example as an additional proof of concept to inspire others interested in creating FPS apps with Codea; it uses a simple 3D model, and I did not developed into a game. As I continue to learn Craft and how to program 3D movement, I hope to create a full fledged game with my own 3D model that I will upload to the repository of Codea games.

@Bri_G , @binaryblues , @dave1707,
I set up a YouTube channel so that I could upload demo videos and attach them to this forum.

The first video attached is the 3D FPS maze project running.

The second video is a video of my further work on the project. I attached a 3D model (dragon) as an avatar to the FPS camera so that the 3D FPS camera now represents a flying dragon which I demonstrate flying to an abandoned pyramid. Still no collision physics so I fly through the walls. It doesn’t look like Codea has the ability animate sub-meshes separate so I created the simple illusion of flying by raising and lowering the dragon model (z coordinates) attached to the 3D FPS camera every time the user hits movement key on keyboard:

U = up, M = down, J/L = left/right, I/K = forward/backward and touch screen to pan

If anyone would like to attach the cool-looking dragon avatar to this 3DFPS demo project, all they need to do is

  1. download the free dragon .obj files from:

https://www.turbosquid.com/3d-models/acnologia-3d-1442853#

  1. add add the three textures from the same site that I’ve attached to this post

  2. add addition code to the 3D FPS Movement demo project such as:

Avatar = scene:entity()
Avatar.model = craft.model(asset.documents.aca)
Avatar.scale = vec3(0.005,0.005,0.005)
Avatarbody1 = Avatar.model:getMaterial(1)
Avatarbody2 = Avatar.model:getMaterial(2)
Avatarbody3 = Avatar.model:getMaterial(3)
Avatarbody1 = craft.material(asset.builtin.Materials.Basic)
Avatarbody2 = craft.material(asset.builtin.Materials.Basic)
Avatarbody3 = craft.material(asset.builtin.Materials.Basic)
Avatarbody1.map= readImage(asset.documents.body)
Avatarbody2.map = readImage(asset.documents.body2)
Avatarbody3.map = readImage(asset.documents.wings)
Avatar.model:setMaterial(Avatarbody1, 1)
Avatar.model:setMaterial(Avatarbody2, 2)
Avatar.model:setMaterial(Avatarbody3, 3)
— Position the avatar slightly below (y) and in front of (x) the FPS camera
Avatar.position = vec3(0,-0.5,0.75)

-- Link "Cameras" dependency to this project; dependency contains FirstPersonViewer class that you can add to scene camera to rotate camera view by touching the screen
-- Note: it helps to assign FirstPersonViewer camera to a variable so that can set its properties (e.g. v = scene.camera:add(FirstPersonViewer))

v = scene.camera:add(FirstPersonViewer)

-- Set initial FPS camera position -40 units back from object (e.g. large Pyramid)
v.camera.z = -40

-- attach Avatar model to the FirstPersonViewer camera; Avatar.position then becomes relative to v.camera
Avatar.parent = v.camera

Fyi. :-)[https://youtu.be/jDrTveiny7Y](https://youtu.be/QqnZ_g-LIawhttps://youtu.be/jDrTveiny7Y”)

Sorry, the 3D maze link didn’t upload (still getting the hang of posting).

Here are the links again:

3D Maze

https://youtu.be/jDrTveiny7Y

3D AvatarDragonFlying FPS

https://youtu.be/QqnZ_g-LIaw

@SugarRay You can copy the obj file to the project local folder, then it will packed within the project zip file.

The below shows how to do it:
https://youtu.be/rRKipXZFS9U

@SugarRay - neat demo and videos. You’ve obviously picked up YouTube posting without problem. I think a better video could be if you record the dragon with fixed wing positions and gliding around the scene, tilting the dragon to indicate turning - a bit like a plane.

I took my Ship and Walls game and stripped it down to create this. Move your finger on the screen to move forward/backward or turn left/right. The walls are just randomly placed so just go thru them if you want.

viewer.mode=FULLSCREEN

function setup()
    assert(craft, "Include Craft as a dependency")
    ex,ey,ez=0,45,0
    speed,xx=0,0
    cameraX,cameraY,cameraZ=-205,1,-205
    scene = craft.scene()
    scene.camera.position = vec3(cameraX,0,cameraZ)
    scene.camera.eulerAngles=vec3(ex,ey,ez)
    scene.sun.rotation = quat.eulerAngles(45,0,45)
    scene.ambientColor = color(90,90,90)   
    skyMaterial = scene.sky.material
    skyMaterial.horizon = color(0, 203, 255, 255)
    mtl = craft.material(asset.builtin.Materials.Standard)
    img = readImage(asset.builtin.Surfaces.Basic_Bricks_AO)    
    createFloor()
    -- create random walls
    for z=1,100 do
        xPos=math.random(-200,200)
        zPos=math.random(-200,200)
        createWall(xPos,zPos,math.random(20,60),.1)
        xPos=math.random(-200,200)
        zPos=math.random(-200,200)
        createWall(xPos,zPos,.1,math.random(20,60))
    end    
end

function draw()
    update(DeltaTime)
    scene:draw()
    updateCameraPos()
end

function update(dt)
    scene:update(dt)
    scene.camera.position = vec3(cameraX,cameraY,cameraZ)
    scene.camera.eulerAngles=vec3(ex,ey,ez)
end

function touched(t)
    if t.state==BEGAN then
        tx,ty=t.x,t.y
    elseif t.state==CHANGED then
        xx=(t.x-tx)/300
        speed=(t.y-ty)/300
    elseif t.state==ENDED then
        speed,xx=0,0
    end
end

function updateCameraPos()
    ey=ey-xx
    x=speed*math.sin(math.rad(ey))
    z=speed*math.cos(math.rad(ey))
    cameraX=cameraX+x
    cameraZ=cameraZ+z    
end

function createFloor()
    local c1=scene:entity()
    c1.model = craft.model.cube(vec3(400,.1,400))
    c1.position=vec3(0,0,0)
    c1.material = craft.material(asset.builtin.Materials.Standard)
    c1.material.map = readImage(asset.builtin.Surfaces.Desert_Cliff_Color)
end

function createWall(xp,zp,xs,zs)
    local c1=scene:entity()   
    c1.material=mtl
    c1.material.map=img
    c1.model=craft.model.cube(vec3(xs,6,zs))
    c1.position=vec3(xp,2,zp)
end

Thanks, @binaryblues , @Bri_G , and @dave1707, I think I’m catching on.

  1. I was able to make a zip of my project with my 3D model included as binaryblues suggested and was planning to attach it but the zip file turned out to be too large (likely due to the size of the 3D model) :frowning:

  2. That makes sense, Bri_G, to add some “flying motions” to the avatar in 3D as he is a dragon after all. I played around a little bit and added a left and right banking motion to the avatars left and right turns like you suggested. Would be a lot smoother with continuous motion but for now I wanted to get the hang of moving in 3D so I’m just having the model move once with every key press. I uploaded a video of the avatar with banking:

https://youtu.be/GzAgbUwHGkQ

Having said that, I like dave1707’s FPS movement much better than what I put together— very smooth. I’ve uploaded a video of dave1707’s FPS movement code and attached his code above as a zip project as a convenience for others:

https://youtu.be/dB65YxMOueQ

Now I’m going to spend sometime understanding what dave1707 wrote in his code :-). I’m going to try and modify it to give me stepwise motion with the keyboard along with the ability to move up and down in 3D. I’d like such 3D movement ability in order to move around in projects that have varied terrain.