游戏效果



  • 使用键盘上方向键向前移动
  • 使用键盘左右方向键调整 朝向

游戏原理说明

原理很简单,如效果图所示,主要就是以角色视角方向发射大量的直线模拟视线,并计算直线与墙壁交点,获取每一条视线方向下,角色到墙壁的距离,然后根据近大远小的原理绘制不同长度的竖向直线模拟墙壁。

第一代程序计算交点方法

第一代程序是使用的polyshape对象(二维多边形对象)制作墙壁,polyshape对象重载了函数intersect,可以直接求出直线与多边形的交集,程序编写更加简洁方便,给个检测直线和多边形交点的官方例子:

创建一个矩形多边形和一个线段:

poly1=polyshape([0 0 1 1],[1 0 0 1]);
lineseg=[-1 -1;1.5 1.5];

计算该多边形与线段的交集,并确定线段的哪些部分在多边形的内部,哪些在其外部。

[in,out] = intersect(poly1,lineseg);
plot(poly1)
hold on
plot(in(:,1),in(:,2),'b',out(:,1),out(:,2),'r')
legend('Polygon','Inside','Outside','Location','NorthWest')

我们可以把内部第一个点和最后一个点看作与多边形边缘的交点:

intersectPnt=[in(1,:);in(end,:)]

intersectPnt =
0 0
1 1

但是!:这样的计算方法较缓慢,而且polyshape对象推出时间较晚(至少需要R2017B),
于是该方案被舍弃!!
于是该方案被舍弃!!
于是该方案被舍弃!!

第二代程序计算交点方法

假设角色当前位置为p0=(x0,y0)p_0=(x_0,y_0)p0​=(x0​,y0​),角色当前面向方向为θ0\theta_0θ0​,将所有的墙壁边缘离散成点集合,其点到角色位置方向向量为:
V=[x1−x0y1−y0x2−x0y2−y0⋮⋮xN−x0yN−y0]V=\left[\begin{array}{cc}x_1-x_0&y_1-y_0\\x_2-x_0&y_2-y_0\\\vdots&\vdots\\x_N-x_0&y_N-y_0\end{array}\right]V=⎣⎢⎢⎢⎡​x1​−x0​x2​−x0​⋮xN​−x0​​y1​−y0​y2​−y0​⋮yN​−y0​​⎦⎥⎥⎥⎤​

每个视线方向向量
T=[cos⁡(θ1+θ0)sin⁡(θ1+θ0)cos⁡(θ2+θ0)sin⁡(θ3+θ0)⋮⋮cos⁡(θM+θ0)sin⁡(θM+θ0)]T=\left[\begin{array}{cc}\cos(\theta_1+\theta_0)&\sin(\theta_1+\theta_0)\\\cos(\theta_2+\theta_0)&\sin(\theta_3+\theta_0)\\\vdots&\vdots\\\cos(\theta_M+\theta_0)&\sin(\theta_M+\theta_0)\end{array}\right]T=⎣⎢⎢⎢⎡​cos(θ1​+θ0​)cos(θ2​+θ0​)⋮cos(θM​+θ0​)​sin(θ1​+θ0​)sin(θ3​+θ0​)⋮sin(θM​+θ0​)​⎦⎥⎥⎥⎤​

做内积:
INN=VT′INN=VT' INN=VT′
那么INN矩阵的每个数值都是角色到一个墙壁点方向向量与角色某一视线方向做内积,要考虑到视线为单方向射线,因此将内积为负数的值置为无穷大:INN(INN<0)=inf,之后因为明显和视线越垂直算出的距离越短,但这不是我们想要的,因此要添加新的约束,就是内积值和实际的点到角色的距离差值要小于一定阈值1e-5,添加这个约束后,找到INN矩阵每一列的最小值即可找到每一视线方向最近的墙壁点。

距离转换为线段长度

使用的如下公式:
len=αL∣cos⁡(θ)∣len=\frac{\alpha}{L|\cos(\theta)|} len=L∣cos(θ)∣α​
其中α\alphaα为常数,LLL为墙壁点到角色距离,θ\thetaθ为视线和角色面朝方向的夹角。

完整代码

function maze2_5D_v2
% @author : slandarer
% @公众号 : slandarer随笔
% @知乎   : hikari
help maze2_5D%% ========================================================================
% figure窗口创建
fig=figure();
fig.Position=[50,60,1200,600];
fig.Name='maze 2.5D by slandarer';
fig.NumberTitle='off';
fig.MenuBar='none';
% 俯视图axes坐标区域
ax2D=axes('Parent',fig);
ax2D.XTick=[];ax2D.XColor='none';
ax2D.YTick=[];ax2D.YColor='none';
ax2D.XLim=[0,15];
ax2D.YLim=[0,15];
ax2D.Color=[0,0,0];
ax2D.Position=[0,0,1/2,1];
hold(ax2D,'on')
% 伪3D图axes坐标区域
ax3D=axes('Parent',fig);
ax3D.XTick=[];ax2D.XColor='none';
ax3D.YTick=[];ax2D.YColor='none';
ax3D.XLim=[0,10];
ax3D.YLim=[0,10];
ax3D.Color=[0,0,0];
ax3D.Position=[1/2,0,1/2,1];
hold(ax3D,'on')
%% ========================================================================
% 左侧俯视地图初始化
mazeMat=[1 1 1 1 1 1 1 1 1 1 1 1 1 1 1;1 0 0 0 0 0 1 0 1 0 1 0 0 0 1;1 1 1 1 1 0 1 0 0 0 1 1 1 0 1;1 0 0 0 1 0 1 0 1 0 1 0 0 0 1;1 0 1 1 1 0 0 0 1 0 0 0 1 1 1;1 0 0 0 0 2 1 1 1 0 1 1 1 0 1;1 0 1 0 1 1 1 0 1 0 0 0 1 0 1;1 0 1 0 0 0 0 0 1 1 1 0 1 0 1;1 0 1 1 1 1 1 1 1 0 1 0 0 0 1;1 0 1 0 0 0 0 0 0 0 1 0 1 0 1;1 0 1 1 1 1 1 0 1 0 1 0 1 0 1;1 0 1 0 1 0 1 0 1 0 0 0 1 0 1;1 0 1 0 1 0 1 0 1 1 1 1 1 0 1;1 0 0 0 0 0 0 0 1 0 0 0 0 0 1;1 1 1 1 1 1 1 1 1 1 1 1 1 1 1];
[rowList,colList]=find(mazeMat==1);
sqX=[-1;0;0;-1];sqY=[-1;-1;0;0];
LSList=linspace(-1,0,250)';
NMList=-ones(size(LSList));
FLList=[[LSList,NMList];[LSList,NMList.*0];[NMList,LSList];[NMList.*0,LSList]];BLOCK.pntSet=zeros(2,0);
for n=1:length(rowList) fill(ax2D,sqX+colList(n),sqY+size(mazeMat,1)+1-rowList(n),[1,1,1].*0.9);BLOCK.pntSet=[BLOCK.pntSet;FLList+repmat([colList(n),size(mazeMat,1)+1-rowList(n)],[size(FLList,1),1])];
end
% -------------------------------------------------------------------------
% 角色创建
[trow,tcol]=find(mazeMat==2);
ROLEP.xpos=tcol-0.5;
ROLEP.ypos=size(mazeMat,1)+0.5-trow;
ROLEP.theta=pi/2;
ROLEP.triX=cos([pi/3,pi,-pi/3]).*0.15;
ROLEP.triY=sin([pi/3,pi,-pi/3]).*0.15;
[tX,tY]=rotateData(ROLEP.triX,ROLEP.triY,ROLEP.theta);
ROLEP.pfill=fill(ax2D,tX+ROLEP.xpos,tY+ROLEP.ypos,[1,1,1]);
ROLEP.pshape=polyshape(tX+ROLEP.xpos,tY+ROLEP.ypos);
ROLEP.viewRange=size(mazeMat,1)*sqrt(2);
%% ========================================================================
% 线条创建
% plot(ax3D,[1,1].*10.*i./length(thetaListV),[5-tLen/2,5+tLen/2],...
% 'LineWidth',1.5,'Color',[1,1,1]./10.*tLen,'Tag','blockLine');
% plot(ax2D,[RP.xpos,RP.xpos+cos(thetaList(i))*abs(minList(i))],[RP.ypos,RP.ypos+sin(thetaList(i))*abs(minList(i))])
lineNum=300;for n=1:lineNumPLINE.plotLine3(n)=plot(ax3D,[1,1].*10.*n./lineNum,[-1,-1],'LineWidth',1.5);PLINE.plotLine2(n)=plot(ax2D,[-1,-1],[-1,-1],'Color',lines(1));
enddraw3D(ROLEP,BLOCK,PLINE,lineNum)
%% ========================================================================
% 角色移动函数
set(fig,'KeyPressFcn',@key)
function key(~,event)%按键函数switch event.Keycase 'uparrow'ROLEP.xpos=ROLEP.xpos+cos(ROLEP.theta).*.2;ROLEP.ypos=ROLEP.ypos+sin(ROLEP.theta).*.2;case 'leftarrow'ROLEP.theta=ROLEP.theta+pi/20;case 'rightarrow'ROLEP.theta=ROLEP.theta-pi/20;end[tX,tY]=rotateData(ROLEP.triX,ROLEP.triY,ROLEP.theta);ROLEP.pfill.XData=tX+ROLEP.xpos;ROLEP.pfill.YData=tY+ROLEP.ypos;ROLEP.pshape=polyshape(tX+ROLEP.xpos,tY+ROLEP.ypos);draw3D(ROLEP,BLOCK,PLINE,lineNum)
end
%% ========================================================================
% 视角检测及伪3D图绘制function draw3D(RP,BK,PLINE,LN)% delete(findobj('Tag','blockLine'))thetaListV=linspace(pi/3,-pi/3,LN);thetaList=thetaListV+RP.theta;% 内积法计算距离cutoff=1e-5;cosList=cos(thetaList);sinList=sin(thetaList);vecList=BK.pntSet-[RP.xpos,RP.ypos];disMat=vecList*[cosList;sinList];disMat(disMat<0)=inf;normList=vecnorm(vecList')';diffMat=abs(disMat-repmat(normList,[1,size(disMat,2)]));disMat(diffMat>cutoff)=inf;minList=min(abs(disMat));% 图像重绘for i=1:length(thetaList)tLen=10/abs(minList(i))/abs(cos(thetaListV(i))).*0.6;tLen(tLen>10)=10;PLINE.plotLine3(i).Color=[1,1,1]./10.*tLen;PLINE.plotLine3(i).YData=[5-tLen/2,5+tLen/2];PLINE.plotLine2(i).XData=[RP.xpos,RP.xpos+cos(thetaList(i))*abs(minList(i))];PLINE.plotLine2(i).YData=[RP.ypos,RP.ypos+sin(thetaList(i))*abs(minList(i))];end
end
%% ========================================================================
% 数据旋转角度
function [X,Y]=rotateData(X,Y,theta)rotateMat=[cos(theta),-sin(theta);sin(theta),cos(theta)];XY=rotateMat*[X;Y];X=XY(1,:);Y=XY(2,:);
end
end

目前还没有加检测碰到墙的设置,所以是可以穿墙的,碰撞设置请自行编写,另外视角有点鱼眼的感觉是因为为了方便编写并没有考虑各个竖线的横向分布,如果考虑横向分布的话代码是这样的:

function maze2_5D_v3
% @author : slandarer
% @公众号 : slandarer随笔
% @知乎   : hikari
help maze2_5D_v3%% ========================================================================
% figure窗口创建
fig=figure();
fig.Position=[50,60,1200,600];
fig.Name='maze 2.5D by slandarer';
fig.NumberTitle='off';
fig.MenuBar='none';
% 俯视图axes坐标区域
ax2D=axes('Parent',fig);
ax2D.XTick=[];ax2D.XColor='none';
ax2D.YTick=[];ax2D.YColor='none';
ax2D.XLim=[0,15];
ax2D.YLim=[0,15];
ax2D.Color=[0,0,0];
ax2D.Position=[0,0,1/2,1];
hold(ax2D,'on')
% 伪3D图axes坐标区域
ax3D=axes('Parent',fig);
ax3D.XTick=[];ax2D.XColor='none';
ax3D.YTick=[];ax2D.YColor='none';
ax3D.XLim=[0,10];
ax3D.YLim=[0,10];
ax3D.Color=[0,0,0];
ax3D.Position=[1/2,0,1/2,1];
hold(ax3D,'on')
%% ========================================================================
% 左侧俯视地图初始化
mazeMat=[1 1 1 1 1 1 1 1 1 1 1 1 1 1 1;1 0 0 0 0 0 1 0 1 0 1 0 0 0 1;1 1 1 1 1 0 1 0 0 0 1 1 1 0 1;1 0 0 0 1 0 1 0 1 0 1 0 0 0 1;1 0 1 1 1 0 0 0 1 0 0 0 1 1 1;1 0 0 0 0 2 1 1 1 0 1 1 1 0 1;1 0 1 0 1 1 1 0 1 0 0 0 1 0 1;1 0 1 0 0 0 0 0 1 1 1 0 1 0 1;1 0 1 1 1 1 1 1 1 0 1 0 0 0 1;1 0 1 0 0 0 0 0 0 0 1 0 1 0 1;1 0 1 1 1 1 1 0 1 0 1 0 1 0 1;1 0 1 0 1 0 1 0 1 0 0 0 1 0 1;1 0 1 0 1 0 1 0 1 1 1 1 1 0 1;1 0 0 0 0 0 0 0 1 0 0 0 0 0 1;1 1 1 1 1 1 1 1 1 1 1 1 1 1 1];
[rowList,colList]=find(mazeMat==1);
sqX=[-1;0;0;-1];sqY=[-1;-1;0;0];
LSList=linspace(-1,0,250)';
NMList=-ones(size(LSList));
FLList=[[LSList,NMList];[LSList,NMList.*0];[NMList,LSList];[NMList.*0,LSList]];BLOCK.pntSet=zeros(2,0);
for n=1:length(rowList) fill(ax2D,sqX+colList(n),sqY+size(mazeMat,1)+1-rowList(n),[1,1,1].*0.9);BLOCK.pntSet=[BLOCK.pntSet;FLList+repmat([colList(n),size(mazeMat,1)+1-rowList(n)],[size(FLList,1),1])];
end
% -------------------------------------------------------------------------
% 角色创建
[trow,tcol]=find(mazeMat==2);
ROLEP.xpos=tcol-0.5;
ROLEP.ypos=size(mazeMat,1)+0.5-trow;
ROLEP.theta=pi/2;
ROLEP.triX=cos([pi/3,pi,-pi/3]).*0.15;
ROLEP.triY=sin([pi/3,pi,-pi/3]).*0.15;
[tX,tY]=rotateData(ROLEP.triX,ROLEP.triY,ROLEP.theta);
ROLEP.pfill=fill(ax2D,tX+ROLEP.xpos,tY+ROLEP.ypos,[1,1,1]);
ROLEP.pshape=polyshape(tX+ROLEP.xpos,tY+ROLEP.ypos);
ROLEP.viewRange=size(mazeMat,1)*sqrt(2);
%% ========================================================================
% 线条创建
% plot(ax3D,[1,1].*10.*i./length(thetaListV),[5-tLen/2,5+tLen/2],...
% 'LineWidth',1.5,'Color',[1,1,1]./10.*tLen,'Tag','blockLine');
% plot(ax2D,[RP.xpos,RP.xpos+cos(thetaList(i))*abs(minList(i))],[RP.ypos,RP.ypos+sin(thetaList(i))*abs(minList(i))])
lineNum=300;ttV=linspace(pi/3,-pi/3,lineNum);
for n=1:lineNumPLINE.plotLine3(n)=plot(ax3D,-[1,1].*sin(ttV(n)).*10+5,[-1,-1],'LineWidth',3.5);PLINE.plotLine2(n)=plot(ax2D,[-1,-1],[-1,-1],'Color',lines(1));
enddraw3D(ROLEP,BLOCK,PLINE,lineNum)
%% ========================================================================
% 角色移动函数
set(fig,'KeyPressFcn',@key)
function key(~,event)%按键函数switch event.Keycase 'uparrow'ROLEP.xpos=ROLEP.xpos+cos(ROLEP.theta).*.2;ROLEP.ypos=ROLEP.ypos+sin(ROLEP.theta).*.2;case 'leftarrow'ROLEP.theta=ROLEP.theta+pi/20;case 'rightarrow'ROLEP.theta=ROLEP.theta-pi/20;end[tX,tY]=rotateData(ROLEP.triX,ROLEP.triY,ROLEP.theta);ROLEP.pfill.XData=tX+ROLEP.xpos;ROLEP.pfill.YData=tY+ROLEP.ypos;ROLEP.pshape=polyshape(tX+ROLEP.xpos,tY+ROLEP.ypos);draw3D(ROLEP,BLOCK,PLINE,lineNum)
end
%% ========================================================================
% 视角检测及伪3D图绘制function draw3D(RP,BK,PLINE,LN)% delete(findobj('Tag','blockLine'))thetaListV=linspace(pi/3,-pi/3,LN);thetaList=thetaListV+RP.theta;% 内积法计算距离cutoff=1e-5;cosList=cos(thetaList);sinList=sin(thetaList);vecList=BK.pntSet-[RP.xpos,RP.ypos];disMat=vecList*[cosList;sinList];disMat(disMat<0)=inf;normList=vecnorm(vecList')';diffMat=abs(disMat-repmat(normList,[1,size(disMat,2)]));disMat(diffMat>cutoff)=inf;minList=min(abs(disMat));% 图像重绘for i=1:length(thetaList)tLen=10/abs(minList(i))/abs(cos(thetaListV(i))).*1;tLen(tLen>10)=10;PLINE.plotLine3(i).Color=[1,1,1]./10.*tLen;PLINE.plotLine3(i).YData=[5-tLen/2,5+tLen/2];PLINE.plotLine2(i).XData=[RP.xpos,RP.xpos+cos(thetaList(i))*abs(minList(i))];PLINE.plotLine2(i).YData=[RP.ypos,RP.ypos+sin(thetaList(i))*abs(minList(i))];end
end
%% ========================================================================
% 数据旋转角度
function [X,Y]=rotateData(X,Y,theta)rotateMat=[cos(theta),-sin(theta);sin(theta),cos(theta)];XY=rotateMat*[X;Y];X=XY(1,:);Y=XY(2,:);
end
end

MATLAB | 我用MATLAB制作了一款伪3D第一视角迷宫小游戏相关推荐

  1. 几款最新的解谜单机小游戏

    2019独角兽企业重金招聘Python工程师标准>>> 找到几款新出的解谜类小游戏分享一下. 美女餐厅侦探社 游戏简介     "餐厅小镇"的众多商铺中也会发生一 ...

  2. 这款跳来跳去的小游戏,是微信小程序的年度大招

    虎嗅注:今天,应该有很多人的朋友圈都被一款跳来跳去的小游戏刷屏了.这是微信小程序年底的大招:"小游戏".其实过去小程序一直没有排斥过游戏,只是在张小龙的规划里,还"时机未 ...

  3. 【神奇的Turtle库】海龟在手—天下我有:这款秘制“海龟闯关”小游戏值得拥有,强烈推荐哦~

    导语 哈喽!大家好!我是木木子~ 纵观之前的文章--我发现了一个特点,很多小伙伴儿都喜欢学习Turtle或游戏代码,没错吧~ 那今天这篇文章就是为这2方面的小伙伴儿精心准备滴!Turtle+游戏一起安 ...

  4. 利用Python制作第一人称射击小游戏 含源代码

    大家好 我是毕加锁 (锁!) 今天教大家利用Python制作第一人称小游戏 涉及知识点 1.sprites 2.pygame混音器 3.图章    4.python基础语法 .代码 1发射声 from ...

  5. html钓鱼游戏源代码,钓鱼才是游戏本体,盘点五款优秀的内置钓鱼小游戏

    在挥舞着刀枪剑戟与怪物或者僵尸搏斗的间隙,或许你有时会想要换一下口味,享受一下别样的游戏挑战.而游戏内置的钓鱼游戏就恰好为我们带来了这种户外活动的轻度体验.虽然有的玩家不喜欢,但我认为钓鱼有着别样的乐 ...

  6. 你的第一个微信小游戏,教你从0开始制作小游戏(一)

    一.下载微信开发者工具和Cocos Creater 1.微信开发者工具 微信开放文档 2.Cocos Creater Cocos引擎_游戏开发引擎 发布流程就是先在cocos中编写你的游戏,然后生成对 ...

  7. java游戏界面制作_软件设计之基于Java的连连看小游戏(二)——游戏基础界面的制作及事件的添加...

    上次完成到游戏首页的制作,今天完成了游戏基础界面的制作以及事件的简单添加.由于功能尚未完全实现,因此游戏界面的菜单列表只是简单地添加了一下,其余菜单列表以及倒计时等在后续的制作中逐一完善. 1.首先在 ...

  8. 【Python妙用】用200行Python代码制作一个迷宫小游戏

    相信大家都玩过迷宫的游戏,对于简单的迷宫,我们可以一眼就看出通路,但是对于复杂的迷宫,可能要仔细寻找好久,甚至耗费数天,然后可能还要分别从入口和出口两头寻找才能找的到通路,甚至也可能找不到通路. 虽然 ...

  9. Maze_AI: 一款基于 Python + Pygame + AI 算法的迷宫小游戏

    (一)课题内容 实现走迷宫. 主要功能为界面显示.上下左右键的响应以及当前步数统计. 通过该课题全面熟悉数组.字符串等的使用,掌握程序设计的基本方法及友好界面的设计. (二)课题要求 1. 基本要求 ...

最新文章

  1. java HashMap的实现原理
  2. python获取列表中元素的索引
  3. 通过少儿英语入门孩子的自信更多
  4. 国际版Azure搭建Windows多种类型***_一.简介及安装服务
  5. 我的docker随笔23:修改容器时区和添加中文支持
  6. JDK8新特性(十二)之并行的Stream流
  7. 《TensorFlow 2.0深度学习算法实战教材》学习笔记(七、Kears高层接口)
  8. Remote System Explorer Operation在eclipse后台一直跑 解决办法
  9. python3.6_发送邮件
  10. Linux Autofs自动挂载服务详解
  11. [环境搭建]-IIS下搭建FTP过程 解决无法连接及534 Policy requires SSL错误
  12. Cadence全家桶Capture+Allegro流程-5-编辑焊盘并制作封装
  13. android极光推送使用,极光推送使用实例(二) Android客户端
  14. python找色_python坐标找色
  15. struct Lnode *next
  16. CSS基础语法和盒模型
  17. Rational Rose--简介
  18. 跟着团子学SAP PS-前台篇-WBS元素介绍及相关操作 CJ20N
  19. 云原生之Kubernetes:18、详解准入控制器
  20. 通给给定旋转轴向量v,旋转角度ang,计算出旋转矩阵

热门文章

  1. 感觉黑客帝国的时代不远了
  2. android tensorflow文字识别身份证识别ocr文字识别商用源码
  3. 解决Margin塌陷问题方法
  4. BZOJ 2216 Poi2011 Lightning Conductor 动态规划
  5. PCB电路板布局方法总结
  6. 积极的心态是成功的起点
  7. IDEA 2021/2022 修改启动画面及设置编辑器背景图片
  8. linux 显卡驱动程序 接口,Linux显卡驱动程序Nvidia 384.59发布,增加对GeForce GT 1030 GPU支持...
  9. python能制作游戏吗_python制作galgame引擎(一)
  10. c语言如何找到进程基址,从0开始学模拟挂(一)--找内存基址,包含原理 _ 脚本