MATLAB プログラムを書いて、見事なコンボを決めよう。

# 金曜日の午後はやる気が出ない。

　前も[こんな出だしで書いた](https://qiita.com/teruqii/items/28b805eb0b2c3fd1b0b3)な。。。

　みんな在宅勤務だし、オフィスに誰もいないし、緊張感がないからであろうか。。ということで、プログラムを書くモチベーションを上げるための環境を作ってみよう。


# e-sports みたいなプログラミング環境を目指そう！

　e-sports って、プレイヤーと操作画面が絶妙に配置されてて、操作してる人と内容が一気に見えるのがいい感じですよね。
というのを「プログラムを書く」という行為にも採用してみましょう！


## ストリーム映像に文字を書こう。

　ライブ映像にプログラムがスムーズに書ければ第一歩かな。Image Acquisition Toolbox と Computer Vision Toolbox と Image Processing Toolbox があれば簡単ですね。

```matlab
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
```

パソコンに内蔵の内向きカメラを使ってこんな感じ。

![Combo1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475339/f6d52903-0bc2-9bf0-cc2b-c65111bfbde8.jpeg)


プログラムを書く人とプログラムが表示されればいいね。
（後ろは誰もいないオフィス。）

Qiita は動画を載せるのが大変なので、頭の中でサクサク動いてると思ってくださいね。


## キーボード経由でプログラムを書けるようにしよう。

　上のままだと決められた内容を表示するだけなので、キータッチでプログラムを書けるようにしましょう。KeyPressFcn を使おう。

```matlab
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
```

こんな感じ。

![Combo2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475339/5985c9a3-58c8-9b6e-4ae6-02dd0bbb655e.jpeg)


書いたり消したりできております！
（心のなかでスイスイ動いていると思って見ましょう。）


# コンボを決めて爽快感を出してみよう！

　プログラムが一気に書けると爽快！ということで、連続でキーを叩いたらコンボ表示が出るようにしたらいいかな。コンボ用に適当な絵があるといいので、あのオレンジの丘みたいな絵をたくさん用意しておこう。

　あのオレンジの丘↓↓

![logo01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475339/e3c22282-f5c5-d201-2d9e-a655f25bc52b.png)

（これは動いておりません。）

## コンボの前準備で画像を用意する。

　とりあえず12枚を自動生成して、現在のフォルダに logo01.png, logo02.png みたいな名前で保存するスクリプトです。

```matlab
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個できてますね。

![logo04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475339/d9a1fc43-abee-f665-6f26-bc5fb2a65cd7.png)



## コンボ数に応じて画像を表示する。

　Computer Vision Toolbox の AlphaBlender を使おう。

```matlab
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
```

　タイピングするたびに、左上にコンボ数が出るようになってればいいね。

![Combo3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475339/eb8e102f-9f1c-f9a6-101b-890c4add6571.jpeg)

　いい感じになりました！


# 間違うとダメージを受ける。

　コンボだけだと適当にキーボードを連打して3000コンボとかなっちゃうので、体力メーターでも置いておいてバックスペースでダメージを受けるようにして、間違いすぎると死ぬ（＝書いたプログラムが全部消える）ようにしよう。

　とりあえず体力は 640 からスタート（ピクセル数）で、20 ピクセルずつ減ります。

```matlab
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
```

　間違ったときに危険な色になる。上にある四角は体力メーター。

![Combo4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475339/c09abecc-745f-ce06-dd12-679963a2bc01.jpeg)


　体力が 0 未満になったら今まで書いたプログラムは全部消えます。緊張感！

![Combo5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475339/fe6d7de7-67e7-24d4-1a9e-06c16a596e6e.jpeg)



# 楽しいので、プログラミングコンテストに使おうかな。

　とりあえず GIF にしてみました。見えるかな。

![ComboMov.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475339/c3ee4174-5055-e726-1130-91d2f2c014cb.gif)

　（色数を落としすぎて顔色が悪い・・・）

　もうちょっと改良すればコンテンストなんかでも使えますね！
　こういうコンテスト環境とかに興味があれば、~~「いいね！」~~　**「LGTM」**しておいてね！