Log In  

Cart #44669 | 2017-09-27 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
31

Step into cyberspace with cutting-edge 3D vector graphics. Battle tanks with your triangular pew-pew cannon. Explode into a number of polygons.

This is an homage to Specter VR, which came packaged with my Macintosh Performa back in the day.
https://www.mobygames.com/game/spectre

Features:
-60 fps
-Motion blur effects
-Blue/Red 3D glasses mode (it actually works)
-Multiple camera modes
-An attempt at writing title music

Issues:
Gameplay is a bit, well, simple. Basically drive around shooting and being shot at.

--Electric Gryphon

P#44653 2017-09-27 01:31 ( Edited 2017-10-02 00:51)

Awesome Electric Gryphon!!! you RULEZ! :D

P#44658 2017-09-27 06:16 ( Edited 2017-09-27 10:16)

Yes!
Anaglyph 3d is my favorite gimmick!

(If anyone ever made a game with anaglyph and a light gun I'd never do anything else.)

Without digging through the whole code, How are you blending the two channels?

P#44662 2017-09-27 10:20 ( Edited 2017-09-27 14:20)

Oh, I see, You're flickering frames at 60fps. I wonder if this will work on PocketCHIP.

Trying it out here on the web player, there might be a bug where after you die it drops back down to 30fps, because the stereographics gets really jittery after I die and go back to the main menu.

P#44664 2017-09-27 10:35 ( Edited 2017-09-27 14:35)

Hi apLundell,

I think I have a memory leak to hunt down / objects that are not getting deleted at game over. Time to snoop around...

3D glasses light gun game you say? Hmmm... I wonder if my Desert Lead game would work at 60 fps.

Update: Fixed the memory leak. Framerate shouldn't drop between games.

P#44666 2017-09-27 11:03 ( Edited 2017-09-27 15:20)

I see that you are using a lot the for x in all(set) construct.
My benchmark indicates it is mucho slower than for i=1,#set do...end
Any reason you are sticking to this construct?

For info, my dynamic list pattern is the following:

local set={c=0}
function set:add(elt)
 self.c+=1
 self[self.c]=elt
end
function self:apply(fn)
 local n=self.c
 self.c=0 -- virtually clear dataset
 for i=1,n do
  local elt=self[i]
  if fn(elt) then
   self.c+=1
   self[self.c]=elt
  end
 end
end
P#44680 2017-09-27 15:36 ( Edited 2017-09-27 19:36)

It's really faster to repack the list then it is to to use in/all?

P#44682 2017-09-27 15:59 ( Edited 2017-09-27 19:59)

In places where I needed to optimize speed, I have mostly switched to: "for i=1, #set do"
But I'll still use the: "for item in all(list) do" construct initially because it reads more clearly, and it handles deletions dynamically out of the box. (I'll keep your snippet in mind though if I need the boost.)

P#44694 2017-09-27 18:20 ( Edited 2017-09-27 22:20)

I see that you are using a lot the for x in all(set) construct.
My benchmark indicates it is mucho slower than for i=1,#set do...end

@freds72 - This is good to know! I've been using the for..all() to save on tokens but in cases where token space is available, great to know the other loop syntax is faster.

P#44695 2017-09-27 18:52 ( Edited 2017-09-27 22:52)

Sorry to continue derailing the thread on particle pattern, but here a crude benchmark for 128 particles:

  • mode 1: my "reuse" collection pattern :)
  • mode 2: standard add/del pattern
  • mode 3: memory backed collection (0x4300)

Results: my pattern wins :), close second add/del (but slightly unstable cpu usage), last memory (disapointed!)
Complementary screenshots (just because) and code:


Electricgryphon: now that I know a pico-8 god laid his eyes on my code, I can die a happy man :)

"Bench" code:

-- mini benchmark to compare various collection patterns
-- coll: index buffer
-- parts: add/del
-- memparts: index buffer using user memory

-- global time
local time_t=0
-- target particle count
local n=128

local coll={c=0}
function coll:len()
    return coll.c
end
function coll:spawn(x,y)
    coll.c+=1
    coll[coll.c]={
        x=x,y=y,
        t=time_t+120+rnd(16),
        dx=rnd(2)>1 and 1 or -1,
        dy=rnd(2)>1 and 1 or -1}
end
function coll:update()
    local k=coll.c
    coll.c=0
    for i=1,k do
        local p=coll[i]
        if p.t<time_t then
        else
            p.x+=p.dx
            if(p.x<1 or p.x>127) p.dx=-p.dx
            p.y+=p.dy
            if(p.y<1 or p.y>127) p.dy=-p.dy
            coll.c+=1
            coll[coll.c]=p
        end
    end
end
function coll:draw()
    for i=1,coll.c do
        local p=coll[i]
        pset(p.x,p.y,7)
    end
end

local parts={}
function parts:len()
    return #parts
end
function parts:spawn(x,y)
    add(parts,{
        x=x,y=y,
        t=time_t+120+rnd(16),
        dx=rnd(2)>1 and 1 or -1,
        dy=rnd(2)>1 and 1 or -1})
end

function parts:update()
    for p in all(parts) do
        if p.t<time_t then
            del(parts,p)
        else
            p.x+=p.dx
            if(p.x<1 or p.x>127) p.dx=-p.dx
            p.y+=p.dy
            if(p.y<1 or p.y>127) p.dy=-p.dy
        end
    end
end

function parts:draw()
    for p in all(parts) do
        pset(p.x,p.y,7)
    end
end

function readbyte(m)
    return peek(m)-128
end
function writebyte(m,i)
    poke(m,i+128)
end
function readint(m)
    return bor(peek(m),shl(peek(m+1),8))
end
function writeint(m,i)
    poke(m,i)
    poke(m+1,shr(i,8))
end
local c=0
local xm=0x4300
local ym=xm+n*2
local tm=ym+n*2
local dxm=tm+n*2
local dym=dxm+n
local memparts={}
function memparts:len()
 return c
end
function memparts:update()
    local x,y,t,dx,dy
    local ii=c-1
    c=0
    for i=0,ii do
        x=readint(xm+i*2)
        y=readint(ym+i*2)
        t=readint(tm+i*2)
        if t>time_t then
            dx=readbyte(dxm+i)
            dy=readbyte(dym+i)
            x+=dx
            if(x<1 or x>127) dx=-dx
            y+=dy
            if(y<1 or y>127) dy=-dy
            writeint(xm+c*2,x)
            writeint(ym+c*2,y)
            writeint(tm+c*2,t)
            writebyte(dxm+c,dx)
            writebyte(dym+c,dy)
            c+=1
        end
    end
end
function memparts:draw()
    local x,y,t
    for i=0,c-1 do
        x=readint(xm+i*2)
        y=readint(ym+i*2)
        t=readint(tm+i*2)
        pset(x,y,7)
    end
end
function memparts:spawn(x,y)
    writeint(xm+c*2,x)
    writeint(ym+c*2,y)
    writeint(tm+c*2,time_t+120+rnd(16))
    writebyte(dxm+c,rnd(2)>1 and 1 or -1)
    writebyte(dym+c,rnd(2)>1 and 1 or -1)
    c+=1
end

local mode={
    coll,parts,memparts}
local mode_i=1
local particles=mode[mode_i]
local perf={}
local perf_ramp={8,11,5}
local perf_name={"cpu","mem","n"}
function _update60()
    if btnp(4) or btnp(5) then
        mode_i=(mode_i+1)%(#mode+1)
        if(mode_i==0) mode_i=1
        particles=mode[mode_i]
    end
    if particles:len()<n then
        particles:spawn(rnd(127),rnd(127))
    end

    particles:update()
    perf[time_t%64+1]={
        stat(1),
        stat(0)/1024,
        particles:len()/n}

    time_t+=1
end

function _draw()
    cls(0)
    particles:draw()

    print("mode:"..mode_i,2,120,7)

    rectfill(0,0,64,32,1)
    local t=64-time_t%64
    line(t,0,t,32,6)
    for k=1,3 do
        local scale=32
        if(perf[1][k]<0.5) scale=64
        if(perf[1][k]<0.25) scale=128
        color(perf_ramp[k])
        for i=1,#perf do
            local p=perf[i]
            pset(64-i,32-scale*p[k])
        end
        print(perf_name[k]..":"..(flr(1000*perf[1][k])/10).."%",2,28+6*k)
    end
end

P#44718 2017-09-28 16:15 ( Edited 2017-09-28 20:17)

Do 3d glasses actually work on this?

P#44749 2017-09-29 08:27 ( Edited 2017-09-29 12:27)

Yes. Not perfectly, but yes.

I tried it with Red/Cyan glasses. The 3d effect was ok. There was some ghosting though. Playing for too long might give you a headache.

It might work better with Red/Green glasses, but those are harder to find, I don't happen to have any on hand.

The depth effect itself is a bit subtle. The difference between near and far objects isn't too extreme. I usually prefer subtle 3d over crazy in-your-face 3d effects, but I think I would have exaggerated this one a little more.

Short version : It works well enough to be a fun gimmick. But don't throw away your Virtual Boy just yet.

P#44750 2017-09-29 10:40 ( Edited 2017-09-29 14:40)

Yeah Red/Green glasses are hard to find these days as the accepted new standard is cyan/red. (Even Minecraft's Anaglyph mode uses the C/R option.)

But there is hope!

https://www.aliexpress.com/store/product/EastVita-Red-Blue-3D-Glasses-Anaglyph-Framed-3D-Vision-Glasses-for-Game-Stereo-Movie-Dimensional-Glasses/2882079_32816293677.html

Wherever you shop and whatever kind you get though, You'll find the plastic or metal ones are far superior to the crappy cardboard 'tossaway' ones. Those use cellophane which is not durable, And gets dirty and crinkles easier.

Not meaning to distract, Just anaglyph is awesome. Also be interesting to see this effect used with the "Poculous chip" mod in mind.

And if anyone has an old set of 'active shutter' style glasses and a CHIP a fun project could be timing the refreshes to flash red and cyan filters at the right hz to work with those too through the gpio pins and some tricky timing with the DRAW function. I used to have the Sega set and let me tell you, Zaxxon was a whole other beast playing with those on. X3

P#44837 2017-10-01 20:51 ( Edited 2017-10-02 00:53)

[Please log in to post a comment]

Follow Lexaloffle:          
Generated 2024-03-28 14:15:56 | 0.023s | Q:34