by 今天不飞了
疫情在家闲着无聊写的,游戏本身没有可玩性,仅为给大家分享思路。
MATLAB可以设计一些低性能的游戏。
视频链接《因为疫情被封控十天后,开始用MATLAB养鱼玩儿》
function simple_fish(n)
close all; clc
global flag
% 界面
flag = 0;
figure('position',[0 30 1600 840],'color','w','WindowButtonMotionFcn',@currentPt,'menubar','none');
set(gcf,'WindowButtonDownFcn',@ButtonDownFcn);
set(gcf,'KeyPressFcn',@KeyPressFcn);
IpSld = uicontrol('style','slider','Units','normalized',...
'position',[180 992.5 300 20]/1080,'min',0.01,'max',10,'Value',1.5,'Visible',0);
InSld = uicontrol('style','slider','Units','normalized',...
'position',[610 992.5 300 20]/1080,'min',0,'max',5,'Value',4.1,'Visible',0);
folf = uicontrol('style','togglebutton','Units','normalized',...
'position',[.85 .85 .09 .025],'min',0,'max',1,'Value',1,'String','Follow Fish','Visible',0);
bait = uicontrol('style','togglebutton','Units','normalized','callback',@danger,...
'position',[.85 .8 .09 .025],'min',0,'max',1,'Value',0,'String','Put Bait','Visible',0);
goal = uicontrol('style','togglebutton','Units','normalized','callback',@food,...
'position',[.85 .4 .09 .025],'min',0,'max',1,'Value',0,'String','Shark Goal','Visible',0);
tax = axes('position',[.05 .05 .9 .85],'visible','off'); hold all;
axis(tax,[-10 10 -10 10]*2);
ax = axes('position',[.05 .05 .9 .85],'xcolor','none','ycolor','none'); hold all;
axis(ax,[-10 10 -10 10]*2); grid on; box off;
colormap(ax,'hot');
caxis(ax,[.5 2]);
%% 参数
total_time = 1e6;
dt = 5e-2;
% 数量
if nargin<1 || isempty(n)
n = 50;
end
If = 1e-2;
attrS = 1;
alpha = rand(n,1)*2*pi; % 初始方向
r = 10*randn(n,2); % 初始位置
state = [r alpha]; % 状态
fishL = .6; fishH = .1;
% 形状
xfish = [-.5, -.25, 0, .1, .125, .1, 0, -.25]'*1;
yfish = [0, .075, .1, .05, 0, -.05, -.1, -.075]'/2*1;
% 可视化
pth = patch(xfish + zeros(1,n), yfish + zeros(1,n),zeros(n,1),'edgecolor','none');
baitx = []; baity = [];
baith = plot(0,0,'k+','markersize',18,'linewidth',2,'visible','off');
goalx = []; goaly = [];
goalh = plot(0,0,'bo','markersize',18,'linewidth',2,'visible','off');
cp = [];
replD = fishL*4;
replS = 200;
%% 开始养鱼
t = -dt;
tmax = 100;
while t<tmax
t = t + dt;
if isvalid(ax)
xalim = get(ax,'xlim');
yalim = get(ax,'ylim');
else
break
end
% 属性
Ip = get(IpSld, 'Value');
In = get(InSld, 'Value');
% 方向
e = [cos(state(:,3)) sin(state(:,3))];
dTri = delaunay(state(:,1:2));
vn = sparse(dTri,dTri(:,[2 3 1]),1);
vn = vn | vn';
listI = repmat(1:n,1,n);
ns = listI(vn);
nn = sum(vn);
nnmax = max(nn);
% 鱼群模式
neighborI = full(sparse(nn+1,1:n,n+1));
neighborI = cumsum(neighborI(1:end-1,:),1);
neighborI(neighborI==0) = ns;
neighborI = neighborI';
stateA = [state; nan(1,3)];
phi = reshape(stateA(neighborI,3),n,nnmax) - state(:,3);
rho1 = reshape(stateA(neighborI,1),n,nnmax) - state(:,1);
rho2 = reshape(stateA(neighborI,2),n,nnmax) - state(:,2);
rhon = sqrt(rho1.^2 + rho2.^2);
theta = getangle(state(:,3),rho1,rho2,rhon);
minDist = nanmin(rhon,[],2);
w_vision = nansum((Ip*sin(phi) + rhon.*sin(theta)).*(1 + cos(theta)),2)./nansum(1 + cos(theta),2);
% 目标
if ~isempty(baitx)
for bi = 1:numel(baitx)
xb = baitx(bi); yb = baity(bi);
attrNum = min(n,5);
dv = [xb,yb] - state(:,1:2);
[distf,sortf] = sort(sqrt(sum(dv.^2,2)));
attrD = (distf(attrNum) + distf(1))/2;
for j = 1:attrNum
attrf = sortf(j);
w_bait = getangle(state(attrf,3),dv(attrf,1),dv(attrf,2),distf(j));
w_vision(attrf) = (w_vision(attrf)*exp(attrS*(distf(j) - attrD)) + ...
w_bait)/(1 + exp(attrS*(distf(j) - attrD)));
end
end
end
if ~isempty(cp)
dcpv = cp(1:2) - state(:,1:2);
dcp = sqrt(sum(dcpv.^2,2));
w_cp = getangle(state(:,3),-dcpv(:,2),-dcpv(:,1),dcp);
w_vision = (w_vision + w_cp.*exp(replS*(replD - dcp)))./(1 + exp(replS*(replD - dcp)));
end
w_noise = In*sqrt(dt)*randn(n,1);
otherI = repmat(1:n,1,n);
otherI(1:(n+1):end) = [];
otherI = reshape(otherI,n,n-1);
dZ = state(:,1) + 1i*state(:,2) - reshape(state(otherI,1) + 1i*state(otherI,2),n,n-1);
o_di = reshape(state(otherI,3),n,n-1);
U_complex = sum(exp(1i*o_di)./dZ.^2,2)/pi;
w_complex = sum(imag(exp(1i*(2*state(:,3)+o_di))./dZ.^3),2)*2/pi;
collisionAvoid = 1./(1 + exp((fishH - minDist)/fishH*10));
U = [real(U_complex), -imag(U_complex)]*If.*collisionAvoid;
w_hydro = w_complex*If.*collisionAvoid;
theta_dot = w_vision + w_noise + w_hydro;
rdot = e + U;
% 位移
state = state + [rdot theta_dot]*dt;
set(pth,'XData',xfish.*e(:,1)' - yfish.*e(:,2)' + state(:,1)',...
'YData',xfish.*e(:,2)' + yfish.*e(:,1)' + state(:,2)','CData',sum(rdot.^2,2));
% 中轴
if get(folf,'value')
axis(ax,[xalim yalim] + [(mean(state(:,1))-mean(xalim))*[1 1],...
(mean(state(:,2)) - mean(yalim))*[1 1]]);
end
drawnow;
end
% 没想好写什么
function currentPt(~,~)
end
% 吓唬
function danger(~,~)
if get(bait,'value')
[xb,yb] = ginput(1);
baitx = [baitx xb]; baity = [baity yb];
set(bait,'value',0);
set(baith,'xdata',baitx,'ydata',baity,'visible','on');
end
end
% 投喂
function food(~,~)
if get(goal,'value')
[xb,yb] = ginput(1);
goalx = xb; goaly = yb;
set(goal,'value',0);
set(goalh,'xdata',goalx,'ydata',goaly,'visible','on');
end
end
% 鼠标点击
function ButtonDownFcn(~,~)
pt = get(gca,'CurrentPoint');
xb = pt(1,1);
yb = pt(1,2);
if flag
baitx = xb; baity = yb;
else
cp = [xb,yb];
end
end
% 键盘点击
function KeyPressFcn(~,~)
pt = get(gcf,'CurrentCharacter');
if strcmpi(pt,'r')
flag = 1-flag;
if flag
disp('投喂模式')
else
disp('驱散模式')
end
end
if strcmpi(pt,'c')
baitx = [];
baity = [];
cp = [];
end
if strcmpi(pt,'q')
disp('锁定视角')
tmp = get(folf,'Value');
set(folf,'Value',1-tmp)
end
end
end
% 计算偏移角的
function [theta] = getangle(phi,rhox,rhoy,rhom)
if nargin < 4, rhom = sqrt(rhox.^2 + rhoy.^2); end
rhox = rhox./rhom;
rhoy = rhoy./rhom;
ex = cos(phi);
ey = sin(phi);
sgn = sign(ex.*rhoy - ey.*rhox);
sgn(sgn == 0) = 1;
theta = sgn.*acos(ex.*rhox + ey.*rhoy);
end
大概没有什么bug