金曜日の午後はやる気が出ない。
前もこんな出だしで書いたな。。。
みんな在宅勤務だし、オフィスに誰もいないし、緊張感がないからであろうか。。ということで、プログラムを書くモチベーションを上げるための環境を作ってみよう。
e-sports みたいなプログラミング環境を目指そう!
e-sports って、プレイヤーと操作画面が絶妙に配置されてて、操作してる人と内容が一気に見えるのがいい感じですよね。
というのを「プログラムを書く」という行為にも採用してみましょう!
ストリーム映像に文字を書こう。
ライブ映像にプログラムがスムーズに書ければ第一歩かな。Image Acquisition Toolbox と Computer Vision Toolbox と Image Processing Toolbox があれば簡単ですね。
function TextOnVideo
a = imaqfind;
if ~isempty(a)
delete(a)
end
vid = videoinput('winvideo', 1, 'RGB24_1280x720');
vid.TriggerRepeat = Inf;
nBands = get(vid, 'NumberOfBands');
res = get(vid, 'Videoresolution');
start(vid);
F = figure(31415926);
F.CloseRequestFcn = @ReleaseFun;
prepstream = imshow(zeros([res(2) res(1) nBands] ));
hold on
stream = getdata(vid,1);
mask = false(size(stream));
str = 'function o = tameshi(de,su)';
while (ishandle(F) && islogging(vid))
stream = getdata(vid,1);
stream = insertText(stream,[550,100],str,'FontSize',30,'Font','Meiryo');
stream(mask) = 0;
set(prepstream, 'Cdata', stream);
flushdata(vid);
drawnow;
end
function ReleaseFun(~,~)
delete(vid)
delete(F)
end
end
パソコンに内蔵の内向きカメラを使ってこんな感じ。
プログラムを書く人とプログラムが表示されればいいね。
(後ろは誰もいないオフィス。)
Qiita は動画を載せるのが大変なので、頭の中でサクサク動いてると思ってくださいね。
キーボード経由でプログラムを書けるようにしよう。
上のままだと決められた内容を表示するだけなので、キータッチでプログラムを書けるようにしましょう。KeyPressFcn を使おう。
function TextOnVideo
a = imaqfind;
if ~isempty(a)
delete(a)
end
vid = videoinput('winvideo', 1, 'RGB24_1280x720');
vid.TriggerRepeat = Inf;
nBands = get(vid, 'NumberOfBands');
res = get(vid, 'Videoresolution');
start(vid);
F = figure(31415926);
F.CloseRequestFcn = @ReleaseFun;
F.KeyPressFcn = @KeyFun;
prepstream = imshow(zeros([res(2) res(1) nBands] ));
hold on
stream = getdata(vid,1);
mask = false(size(stream));
str = 'function o = tameshi(de,su)';
while (ishandle(F) && islogging(vid))
stream = getdata(vid,1);
stream = insertText(stream,[550,100],str,'FontSize',30,'Font','Meiryo');
stream(mask) = 0;
set(prepstream, 'Cdata', stream);
flushdata(vid);
drawnow;
end
function KeyFun(~,ev)
if strcmp(ev.Key,'backspace')
if ~isempty(str)
str(end) = [];
end
elseif strcmp(ev.Key,'return')
str = [str,newline];
else
str = [str,ev.Character];
end
end
function ReleaseFun(~,~)
delete(vid)
delete(F)
end
end
こんな感じ。
書いたり消したりできております!
(心のなかでスイスイ動いていると思って見ましょう。)
コンボを決めて爽快感を出してみよう!
プログラムが一気に書けると爽快!ということで、連続でキーを叩いたらコンボ表示が出るようにしたらいいかな。コンボ用に適当な絵があるといいので、あのオレンジの丘みたいな絵をたくさん用意しておこう。
あのオレンジの丘↓↓
(これは動いておりません。)
コンボの前準備で画像を用意する。
とりあえず12枚を自動生成して、現在のフォルダに logo01.png, logo02.png みたいな名前で保存するスクリプトです。
for nn = 1:12
L = 20*membrane(nn,25);
logoFig = figure('Color',[0 0 0]);
logoFig.Position = [100 100 200 120];
logoax = axes('CameraPosition', [-193.4013 -265.1546 220.4819],...
'CameraTarget',[26 26 10], ...
'CameraUpVector',[0 0 1], ...
'CameraViewAngle',9.5, ...
'DataAspectRatio', [1 1 .9],...
'Position',[0 0 1 1], ...
'Visible','off', ...
'XLim',[1 51], ...
'YLim',[1 51], ...
'ZLim',[-13 40], ...
'parent',logoFig);
s = surface(L, ...
'EdgeColor','none', ...
'FaceColor',[0.9 0.2 0.2], ...
'FaceLighting','phong', ...
'AmbientStrength',0.3, ...
'DiffuseStrength',0.6, ...
'Clipping','off',...
'BackFaceLighting','lit', ...
'SpecularStrength',1, ...
'SpecularColorReflectance',1, ...
'SpecularExponent',7, ...
'Tag','TheMathWorksLogo', ...
'parent',logoax);
l1 = light('Position',[40 100 20], ...
'Style','local', ...
'Color',[0 0.8 0.8], ...
'parent',logoax);
l2 = light('Position',[.5 -1 .4], ...
'Color',[0.8 0.8 0], ...
'parent',logoax);
z = zoom(logoFig);
z.setAxes3DPanAndZoomStyle(logoax,'camera');
print('-dpng',['logo',num2str(nn,'%02d'),'.png'])
end
close all
現在のフォルダにこんなファイルが12個できてますね。
コンボ数に応じて画像を表示する。
Computer Vision Toolbox の AlphaBlender を使おう。
function ComboSystem
a = imaqfind;
if ~isempty(a)
delete(a)
end
vid = videoinput('winvideo', 1, 'RGB24_1280x720');
vid.TriggerRepeat = Inf;
AB = vision.AlphaBlender;
AB.Operation = 'Binary mask';
AB.MaskSource = 'Input port';
nBands = get(vid, 'NumberOfBands');
res = get(vid, 'Videoresolution');
start(vid);
F = figure(31415926);
F.CloseRequestFcn = @ReleaseFun;
F.KeyPressFcn = @KeyFun;
prepstream = imshow(zeros([res(2) res(1) nBands] ));
hold on
[X,BW] = logomaker(12);
stream = getdata(vid,1);
mask = false(size(stream));
str = '';
Combo = 0;
while (ishandle(F) && islogging(vid))
stream = getdata(vid,1);
if Combo > 0
n = mod(Combo,12) + 1;
stream = AB(stream,X{n},BW{n});
stream = insertText(stream,[75,200],[num2str(Combo),' Combo!'],'FontSize',30);
end
stream = insertText(stream,[550,100],str,'FontSize',30,'Font','Meiryo');
stream(mask) = 0;
set(prepstream, 'Cdata', stream);
flushdata(vid);
drawnow;
end
function [X,BW] = logomaker(n)
for m = 1:n
X{m} = imread(['logo',num2str(m,'%02d'),'.png']);
G = rgb2gray(X{m});
BW{m} = G < 220;
end
end
function KeyFun(~,ev)
if strcmp(ev.Key,'backspace')
if ~isempty(str)
str(end) = [];
end
elseif strcmp(ev.Key,'return')
str = [str,newline];
else
str = [str,ev.Character];
Combo = Combo + 1;
end
end
function ReleaseFun(~,~)
delete(vid)
delete(F)
end
end
タイピングするたびに、左上にコンボ数が出るようになってればいいね。
いい感じになりました!
間違うとダメージを受ける。
コンボだけだと適当にキーボードを連打して3000コンボとかなっちゃうので、体力メーターでも置いておいてバックスペースでダメージを受けるようにして、間違いすぎると死ぬ(=書いたプログラムが全部消える)ようにしよう。
とりあえず体力は 640 からスタート(ピクセル数)で、20 ピクセルずつ減ります。
function MatKen
a = imaqfind;
if ~isempty(a)
delete(a)
end
vid = videoinput('winvideo', 1, 'RGB24_1280x720');
vid.TriggerRepeat = Inf;
AB = vision.AlphaBlender;
AB.Operation = 'Binary mask';
AB.MaskSource = 'Input port';
nBands = get(vid, 'NumberOfBands');
res = get(vid, 'Videoresolution');
start(vid);
F = figure(20200527);
F.CloseRequestFcn = @ReleaseFun;
F.KeyPressFcn = @KeyFun;
prepstream = imshow(zeros([res(2) res(1) nBands] ));
hold on
[X,BW] = logomaker(12);
stream = getdata(vid,1);
mask = false(size(stream));
str = '';
Combo = 0;
HP = 640;
while (ishandle(F) && islogging(vid))
stream = getdata(vid,1);
if Combo > 0
n = mod(Combo,12) + 1;
stream = AB(stream,X{n},BW{n});
stream = insertText(stream,[75,200],[num2str(Combo),' Combo!'],'FontSize',30);
end
stream = insertText(stream,[550,100],str,'FontSize',30,'Font','Meiryo');
stream = insertShape(stream,'rectangle',[300 20 650 40],'LineWidth',8,'Color','w');
stream = insertShape(stream,'FilledRectangle',[305 25 HP 30],'LineWidth',8,'Color','g');
stream(mask) = 0;
set(prepstream, 'Cdata', stream);
flushdata(vid);
drawnow;
if HP < 0
stream = insertText(stream,[160,200],'GAME OVER','FontSize',144,'Font','Meiryo','BoxOpacity',0,'TextColor','w');
set(prepstream, 'Cdata', stream);
flushdata(vid);
drawnow;
break
end
end
function [X,BW] = logomaker(n)
for m = 1:n
X{m} = imread(['logo',num2str(m,'%02d'),'.png']);
G = rgb2gray(X{m});
BW{m} = G < 220;
end
end
function KeyFun(~,ev)
if strcmp(ev.Key,'backspace')
if ~isempty(str)
str(end) = [];
Combo = 0;
HP = HP - 20;
if ~mask(1,1,2)
mask(:,:,2) = true;
mask(:,:,3) = true;
end
end
elseif strcmp(ev.Key,'return')
str = [str,newline];
else
str = [str,ev.Character];
if ~strcmp(ev.Key,'space')
Combo = Combo + 1;
end
if mask(1,1,2)
mask(:,:,2) = false;
mask(:,:,3) = false;
end
end
end
function ReleaseFun(~,~)
delete(vid)
delete(F)
end
end
間違ったときに危険な色になる。上にある四角は体力メーター。
体力が 0 未満になったら今まで書いたプログラムは全部消えます。緊張感!
楽しいので、プログラミングコンテストに使おうかな。
とりあえず GIF にしてみました。見えるかな。
(色数を落としすぎて顔色が悪い・・・)
もうちょっと改良すればコンテンストなんかでも使えますね!
こういうコンテスト環境とかに興味があれば、「いいね!」 **「LGTM」**しておいてね!