【UiPath】フルリモのお供。マウスを動かし続けるロボット（改良） 

## 皆もすなるQiitaといふものを、儂もしてみむとてするなり。

と言う訳で、こん＊＊は！はなっち！です。

この記事はUiPathブログ発信チャレンジ2023サマーの24日目の記事です。

【初めに】
　UiPathブログ発信チャレンジ2023サマーの3日目の記事で、`【UiPath】フルリモのお供。マウスを動かし続けるロボット`を書きました。

https://qiita.com/HANACCHI/items/8d5e14ef9f868c538fc4

その後、実際に使ってみると、じっとしている分には申し分ないのですが、例えば`PowerPointを作成中`で、右下にある`Objectを右上に移動`しようとすると、`勝手にマウスが動いて`しまって、`アタフタ`してしまう事が多々ありました。

本来はスクリーンセーバーが起動するのを抑止しようという目的で作成したものでしたが、このままだと`別のイライラが募る`ばかり...


今回は、その後の対応策をまとめてみました。
- - - 
## １）移動させるマウス位置を固定にしなければいい！
前回の記事では、

>最初のカーソルの位置は(X,Y)=(200,400)としました。次は (X,Y)=(X+α,Y+α) α=1 とすると、いずれは画面サイズを超えたところにカーソル位置を持っていくことになります。

>スクリーンセーバーが起動されなければ、カーソル位置を常に新しい場所にする必要はなく、最低２点あれば十分ですので。int32型配列として保持する事にしました。
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/9e77716c-2d25-0c0a-3a04-6d9a74afd113.png)

と、Int32型配列に、固定値として設定していました。

:::note warn
※今気が付いた。座標の話と張り込んだ画像がマッチしていないですね(^^♪
:::

ここの`固定値`を`現在のマウスの位置`に変更し、マウスを動かす先は`現在のマウスの位置`を基に `+1` したポイントにする事にしました。
- - - 
## ２）現在のマウスの位置をどう取得するのか？
UiPathアクティビティから、`MOUSE`で検索すると...
<img width="350" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/20cdd8ad-7ee6-0bc8-6f04-ba671057867d.png">
ちょっと該当するアクティビティはなさそうです。

となると、.NETの機能を利用するのが手っ取り早いですね。得た結果が、以下の式でした。

```
'マウスポインタの位置を取得する
'X座標を取得する
Dim x As Integer = System.Windows.Forms.Cursor.Position.X
'Y座標を取得する
Dim y As Integer = System.Windows.Forms.Cursor.Position.Y
```
確認のため、**「メッセージボックス」アクティビティ**で、マウスの位置を表示させてみましょう。
〔設定内容〕
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/f3a704ad-7a9e-f047-599d-33dcf7ff8c66.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/889d1133-64c1-a8f4-75a7-5ca89c752e0c.png)
〔実行#1〕
UiPath Studioの「現在のファイルを実行アイコン」の中ほどにマウスを置き、実行！（下図では赤い点の辺り）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/0ab32ae8-ee40-4dea-948c-a5d9c94f081a.png)
〔実行結果#1〕
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/a21ebe1f-060d-52ec-b24d-5cd980f97af3.png)

〔実行#2〕
画面の中央ほどにマウスを置き、実行！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/85150778-525e-4933-3bd9-72ac4ef9c1b8.png)
〔実行結果#2〕
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/164c0d74-155c-2789-7eb2-5f2cbb711307.png)

どうやら、ちゃんと取得できますね。

## ３）取得した現在のマウスの位置から...
スクリーンセーバーが起動が起動しない様に、現在のマウスの位置から `+1` させていきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/6c152257-2c60-68d0-499f-b29b5e422a4e.png)
```
X = {1 + System.Windows.Forms.Cursor.Position.X}
Y = {1 + System.Windows.Forms.Cursor.Position.Y}
```
これを延々繰返していけばOKですね。

ただ、`画面の最大サイズ位置`であるである`右下にマウス`があった場合、異常終了するのではないか？と言う思いにいたり...だったら、`画面の最大サイズ`を求めて、その画面サイズを超えないように制御すれば...

## ４）画面の最大サイズをどう取得するのか？
これも、.NETの機能を利用するのが手っ取り早いですね。得た結果が、以下の式でした。

```
'ディスプレイの高さ
Dim h As Integer = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height
'ディスプレイの幅
Dim w As Integer = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width
```
確認のため、**「メッセージボックス」アクティビティ**で、画面の最大サイズを表示させてみましょう。
〔設定内容〕
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/163af8fd-fb30-576a-1940-3e5dcc8e8eef.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/44f04085-394a-2e5f-bd80-cc3eaf9b729a.png)
〔実行結果〕
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/56da37f9-c069-a539-e1a6-fdd7da45c17a.png)
〔評価〕
マウスを中ほどにして取得した値と、画面サイズを見比べるとちゃんと取れていますね（当然と言えば当然）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/164c0d74-155c-2789-7eb2-5f2cbb711307.png)⇔![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/56da37f9-c069-a539-e1a6-fdd7da45c17a.png)

## ５）計算！
現在のマウスの位置を `+1` させていき、画面サイズを超えたなら `-1` させていく。そうなると、最終的には画面サイズの辺り（右下）を行ったり来たりの動きになってしまいますね。
だったら、繰り返して計算するのだから、
- 奇数回目は、現在のマウスの位置に`+1`する
- 偶数回目は、現在のマウスの位置に`-1`する

ようにしてやれば、解決！

と考えたのですが、第一回目（奇数回目）のマウス位置が、そもそも`画面サイズ`で、そこに`+1`したら既に画面サイズからオーバーする値になってしまう！

と悶々とし始めたら、記事の冒頭の
>`画面の最大サイズ位置`であるである`右下にマウス`があった場合、異常終了するのではないか？と言う思いにいたり...だったら、`画面の最大サイズ`を求めて、その画面サイズを超えないように制御すれば...

の思いを翻して、そもそも画面のサイズを超えても、あるいはあり得ない値が設定されても大丈夫ではないか？と考えはじめ...

## ６）場外乱闘
画面サイズの高さ(.HEIGHT)、幅(.WIDTH)は、`0以上の整数`ですから、究極の場外乱闘は、`負の整数`ですね。
- **「ホバー」アクティビティ** の`Xのオフセット`と`Yのオフセット`の値に、直接`-1`を設定して実行しみます。
<img width="450" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/c46339a0-603e-22ac-5a79-78e258f7ed29.png">

お見せ出来ませんが、画面の左上にマウスが移動しました！！

- **「ホバー」アクティビティ** の`Xのオフセット`と`Yのオフセット`の値に、直接`10000`を設定して実行しみます。
<img width="450" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/ccec032f-c5a9-087c-6b88-85f9d520314f.png">

お見せ出来ませんが、画面の右下上にマウスが移動しました！！

と言う事は、特に場外乱闘値を考慮しなくてもよさそうですね。

## ７）再考した計算！
`現在のマウスの位置`は取得出来た。繰り返しの奇数回には`+1`、偶数回には`-1`をしてあげれば、`現在のマウスの位置`辺りを**ちょろっと**動きます。その`+1`あるいは`-1`を求めるのに、こんな風に考えてみました。

#### その１
奇数回目か、偶数回目かは、**「繰り返し (コレクションの各要素)」アクテイビティ**の「現在のインデックス」の値を、２で割った際の余りが`1だったら奇数`、`0だったら偶数回`と言う事はわかりますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/4a1e877e-c2e1-db73-10c1-7142f98162a0.png)
この条件分岐後の処理の中で、現在のマウスの位置を `+1` させていったり、 `-1` させていったりすれば良い訳です。

#### その２
奇数回目か、偶数回目か。状態は2つしかありません。この`2つしかない`ことに着目すると、Boolean型を利用できそうです。

Boolean型は、True値またはFalse値を取りうる変数の型です。Boolean型をInt32型に変換すると、`True値は-1`に、`False値は0`になります。

:::note warn
逆に、Int32型をBoolean型に変換すると、`0はFalse`に、`0以外はTrue`になります。
:::

Boolean型が、`-1または0を持つInt32型の値`に変換出来るのですから、その値を直接利用しない手はありませんね。
```
計算式：CINT(DISTANCE) * 2 + 1

DISTANCE(Boolean型)がTrueの場合
　CINT(DISTANCE) * 2 + 1
= CINT(True) * 2 + 1
= -1 * 2 + 1
= -2 + 1
= -1

DISTANCE(Boolean型)がFalseの場合
　CINT(DISTANCE) * 2 + 1
= CINT(False) * 2 + 1
=  0 * 2 + 1
=  0 + 1
=  1
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/b27d3f64-cfbd-3fbe-da8a-5ba7a7861e40.png)
<img width="450" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/a1979054-b8c9-1913-fc68-6b386480f022.png">
<img width="450" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/a862f246-cf6a-e1a6-64b4-cd3db3525180.png">
<img width="450" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/60f80b47-0a78-3b56-f4f4-e40f6b321a48.png">

## ８）態々計算する必要ある？
今まで、スクリーンセーバが起動されないためには、マウスを**移動させなければならない**と考えていました。

ここまで来て今更なのですが、もしかしたら、マウスを**移動させなければならない**のではなく、マウスを**移動する行為をしなければならない**のではないかと。

`+1`あるいは`-1`したマウスの位置をもって **「ホバー」アクティビティ**を実行しなくても、`±0`したマウスの位置をもって、つまり今のマウスの位置をそのまま設定し、 **「ホバー」アクティビティ**を実行すればいいのではないかと｡｡｡

やってみたら、正にその方法で正解でした。
<img width="450" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669015/cc2193ec-3647-7645-eeda-547a563bb8bd.png">
こうすると、実にスッキリしたロジックになりますね。

## まとめ
- 一つの完成形から、`不都合`を探してみよう
- その`不都合`から、`改善の方法`を考えてみよう、やってみよう
- `改善の方法`を、`複数トライ`してみよう
- その中で、`最適解`で実装し、しばらく使ってみよう
- また`不都合`を探してみよう
  - 現在の不具合は、手でマウスを移動している最中に、**「ホバー」アクティビティ**が実行され、手でのマウス移動が終了した時に、**「ホバー」アクティビティ**が実行された時点のマウス位置にマウス移動してしまう（戻ってしまう）事が、ややイライラのタネですね。


- - - 

## おわりに
いかがでした？

今回も読んでいただきありがとうございました！
是非UiPathでのロボ開発の一助になればと思っています。

ありがとうございました！