找回密码
 中文实名注册
搜索
查看: 77|回复: 4

3D后室

[复制链接]

1

主题

3

回帖

64

积分

注册会员

积分
64
发表于 2026-1-18 12:08:59 | 显示全部楼层 |阅读模式
本帖最后由 陆灏 于 2026-1-18 13:06 编辑

import pygame,sys,math,random,time
W,H=1280,720;F=60
FOV=math.radians(60);RAY=86;MAXD=900;SCALE=W//RAY
TILE=100;C=20;R=16
SP=2.2;SR=1.9;RT=0.9;HP=100
AM=12;DMG=40;RL=0.9
NE=8;EH=100;ES=1.1;ED=450;EA=40;EC=1.2
pygame.init();S=pygame.display.set_mode((W,H));T=pygame.time.Clock();FNT=pygame.font.SysFont(None,15)
def cl(x,a,b): return a if x<a else b if x>b else x
def put(u,p,c=(255,255,255)): S.blit(FNT.render(u,1,c),p)
def gen():
    g=[[1]*C for _ in range(R)];rs=[]
    for _ in range(44):
        w=random.randint(3,7);h=random.randint(3,6)
        x=random.randint(1,C-w-1);y=random.randint(1,R-h-1);r=pygame.Rect(x,y,w,h);ok=1
        for q in rs:
            if r.colliderect(q.inflate(2,2)): ok=0;break
        if not ok: continue
        rs.append(r)
        for yy in range(y,y+h):
            for xx in range(x,x+w): g[yy][xx]=0
    def c0(r): return (r.x+r.w//2,r.y+r.h//2)
    for i in range(1,len(rs)):
        x1,y1=c0(rs[i-1]);x2,y2=c0(rs)
        if random.random()<0.5:
            for xx in range(min(x1,x2),max(x1,x2)+1): g[y1][xx]=0
            for yy in range(min(y1,y2),max(y1,y2)+1): g[yy][x2]=0
        else:
            for yy in range(min(y1,y2),max(y1,y2)+1): g[yy][x1]=0
            for xx in range(min(x1,x2),max(x1,x2)+1): g[y2][xx]=0
    return g,rs
G,RS=gen()
WALL=[pygame.Rect(c*TILE,r*TILE,TILE,TILE) for r in range(R) for c in range(C) if G[r][c]==1]
if RS:
    r=RS[0];SX=(r.x+r.w//2)*TILE+TILE//2;SY=(r.y+r.h//2)*TILE+TILE//2
else: SX,SY=TILE*2+TILE//2,TILE*2+TILE//2
class P:
    def __init__(s,x,y):
        s.x=x;s.y=y;s.a=0.0;s.hp=HP;s.sp=SP
        s.am=AM;s.rl=0
Ply=P(SX,SY)
class E:
    def __init__(s,x,y):
        s.x=x;s.y=y;s.hp=EH;s.ok=1;s.st=0;s.pt=s.rp();s.cd=0
    def rp(s):
        a=random.random()*math.tau;return(s.x+math.cos(a)*180,s.y+math.sin(a)*180)
    def d(s): return math.hypot(s.x-Ply.x,s.y-Ply.y)
    def see(s):
        d=s.d()
        if d>ED: return 0
        st=max(1,int(d/20))
        for i in range(1,st+1):
            t=i/st;sx=s.x+(Ply.x-s.x)*t;sy=s.y+(Ply.y-s.y)*t
            r=pygame.Rect(sx-2,sy-2,4,4)
            for w in WALL:
                if w.colliderect(r): return 0
        return 1
    def upd(s,dt):
        if not s.ok: return
        if s.cd>0: s.cd-=dt
        if s.see(): s.st=1
        else:
            if s.st!=2: s.st=2;s.pt=s.rp()
        if s.st==1:
            a=math.atan2(Ply.y-s.y,Ply.x-s.x)
            nx=s.x+math.cos(a)*ES*60*dt;ny=s.y+math.sin(a)*ES*60*dt
            r=pygame.Rect(nx-8,ny-8,16,16)
            if not any(w.colliderect(r) for w in WALL): s.x,s.y=nx,ny
            else:
                a+=math.pi/2;nx=s.x+math.cos(a)*ES*60*dt;ny=s.y+math.sin(a)*ES*60*dt
                r=pygame.Rect(nx-8,ny-8,16,16)
                if not any(w.colliderect(r) for w in WALL): s.x,s.y=nx,ny
            if s.d()<EA and s.cd<=0: Ply.hp-=10;s.cd=EC
        else:
            tx,ty=s.pt;a=math.atan2(ty-s.y,tx-s.x)
            nx=s.x+math.cos(a)*ES*0.7*60*dt;ny=s.y+math.sin(a)*ES*0.7*60*dt
            r=pygame.Rect(nx-8,ny-8,16,16)
            if not any(w.colliderect(r) for w in WALL): s.x,s.y=nx,ny
            if math.hypot(tx-s.x,ty-s.y)<20: s.pt=s.rp()
EN=[]
for i in range(NE):
    if RS:
        r=random.choice(RS);ex=(r.x+random.randint(0,r.w-1))*TILE+TILE//2;ey=(r.y+random.randint(0,r.h-1))*TILE+TILE//2
    else:
        ex=random.randint(TILE,C*TILE-TILE);ey=random.randint(TILE,R*TILE-TILE)
    EN.append(E(ex,ey))
help_on=0;dead=0
def shoot(dmg):
    mx,my=pygame.mouse.get_pos();dx=mx-W//2;off=(dx/(W//2))*(FOV/2);a=Pl.a+off if (Pl:=Ply) else Ply.a+off
    for depth in range(0,MAXD,8):
        wx=Pl.x+math.cos(a)*depth;wy=Pl.y+math.sin(a)*depth;r=pygame.Rect(wx-2,wy-2,4,4)
        if any(w.colliderect(r) for w in WALL):return 0,None
        for e in EN:
            if e.ok and math.hypot(e.x-wx,e.y-wy)<16:
                e.hp-=dmg
                if e.hp<=0: e.ok=0
                return 1,e
    return 0,None
def blk(x,y):
    r=pygame.Rect(x-6,y-6,12,12);return any(w.colliderect(r) for w in WALL)
last=0;sd=0.25
while 1:
    dt=T.tick(F)/1000.0;k=pygame.key.get_pressed()
    for ev in pygame.event.get():
        if ev.type==pygame.QUIT:pygame.quit();sys.exit()
        if dead:
            if ev.type==pygame.KEYDOWN:
                if ev.key==pygame.K_r:
                    Ply.x,Ply.y=SX,SYly.hp=HPly.am=AMly.rl=0;dead=0
                if ev.key==pygame.K_q:pygame.quit();sys.exit()
                if ev.key==pygame.K_0:help_on^=1
            continue
        if ev.type==pygame.KEYDOWN:
            if ev.key==pygame.K_SPACE:
                now=time.time()
                if Ply.rl<=0:
                    ok=0
                    if Ply.am>0 and now-last>=sdk=1
                    if ok:
                        last=now;d=DMG;shoot(d)ly.am-=1
            if ev.key==pygame.K_rly.rl=RL
            if ev.key==pygame.K_0:help_on^=1
        if ev.type==pygame.MOUSEBUTTONDOWN and not dead:
            if ev.button==1:
                now=time.time()
                if Ply.rl<=0 and Ply.am>0 and now-last>=sd:
                    last=now;d=DMG;shoot(d)ly.am-=1
    if dead:continue
    rrot=RT*dt
    if k[pygame.K_a]ly.a-=rrot
    if k[pygame.K_d]ly.a+=rrot
    if Ply.rl>0:
        Ply.rl-=dt
        if Ply.rl<=0ly.am=AM
    mv=Ply.sp*(SR if (k[pygame.K_LSHIFT] or k[pygame.K_RSHIFT]) else 1.0)
    f=0
    if k[pygame.K_w]:f+=1
    if k[pygame.K_s]:f-=1
    if f:
        nx=Ply.x+math.cos(Ply.a)*f*mv*60*dt;ny=Ply.y+math.sin(Ply.a)*f*mv*60*dt
        if not blk(nx,ny)ly.x,Ply.y=nx,ny
    for e in EN:e.upd(dt)
    if Ply.hp<=0:dead=1ly.hp=0
    S.fill((24,24,24))
    sa=Ply.a-FOV/2
    for i in range(RAY):
        ra=sa+FOV*i/RAY;d=0;hit=0
        while d<MAXD and not hit:
            d+=6
            wx=Ply.x+math.cos(ra)*d;wy=Ply.y+math.sin(ra)*d
            tx=int(wx//TILE);ty=int(wy//TILE)
            if tx<0 or ty<0 or tx>=C or ty>=R:hit=1;break
            if G[ty][tx]==1:hit=1;break
        if hit:
            d*=math.cos(Ply.a-ra)
            if d==0:d=0.0001
            hh=cl(12000/(d+0.0001),2,H)
            sh=255-cl(int(d/6),0,220)
            pygame.draw.rect(S,(sh,sh,sh),(i*SCALE,(H-int(hh))//2,SCALE,int(hh)))
    pygame.draw.circle(S,(255,0,0),(W//2,H//2),4)
    vis=[]
    for e in EN:
        if not e.ok:continue
        dx=e.x-Ply.x;dy=e.y-Ply.y;dist=math.hypot(dx,dy)
        at=math.atan2(dy,dx);rel=at-Ply.a
        while rel<-math.pi:rel+=2*math.pi
        while rel>math.pi:rel-=2*math.pi
        if abs(rel)<FOV/1.2 and dist<MAXD:
            ok=1;stp=max(1,int(dist/20))
            for j in range(1,stp+1):
                t=j/stp;sx=Ply.x+dx*t;sy=Ply.y+dy*t
                if blk(sx,sy)k=0;break
            if ok:vis.append((dist,rel,e))
    vis.sort(key=lambda x:x[0],reverse=True)
    for dist,rel,e in vis:
        sx=W//2+math.tan(rel)*(W//2)
        ph=cl(9000/(dist+1),20,H*0.9);pw=ph*0.5
        rct=pygame.Rect(int(sx-pw/2),int((H-ph)/2),int(pw),int(ph))
        sh=200-int((dist/ED)*150);sh=cl(sh,40,200)
        pygame.draw.rect(S,(30,sh,30),rct)
        hb_w=rct.width;pygame.draw.rect(S,(50,50,50),(rct.x,rct.y-12,hb_w,6))
        pygame.draw.rect(S,(200,30,30),(rct.x,rct.y-12,int(hb_w*cl(e.hp/EH,0,1)),6))
    put(f"HP:{int(Ply.hp)}/{HP}",(6,6));put(f"AM:{Ply.am}",(6,26))
    if help_on:
        put("W/S move A/D turn Shift sprint Space/mouse fire R reload",(W//2-300,H//2-24))
        put("Dead -> press R to respawn, Q to quit",(W//2-300,H//2-4))
    pygame.display.flip()
回复

使用道具 举报

1

主题

3

回帖

64

积分

注册会员

积分
64
 楼主| 发表于 2026-1-18 12:26:30 | 显示全部楼层
本帖最后由 陆灏 于 2026-1-18 12:34 编辑

1111111111
回复

使用道具 举报

1

主题

3

回帖

64

积分

注册会员

积分
64
 楼主| 发表于 2026-1-18 12:43:34 | 显示全部楼层
别买,出了“亿”点小问题
回复

使用道具 举报

4

主题

3

回帖

384

积分

中级会员

积分
384
发表于 3 天前 | 显示全部楼层

回帖奖励 +2 金钱

11111111111111111
回复

使用道具 举报

26

主题

32

回帖

3046

积分

论坛元老

积分
3046
发表于 3 天前 | 显示全部楼层
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 中文实名注册

本版积分规则

快速回复 返回顶部 返回列表