@ganziliang/desktop-pet 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/assets/outfits.json +24 -4
- package/package.json +1 -1
- package/renderer/app.js +12 -10
- package/renderer/fx.js +28 -0
- package/scripts/probe-dim.py +41 -0
- package/scripts/qa-awaken-capture.sh +68 -0
- package/scripts/qa-backdrop.ps1 +14 -0
- package/scripts/qa-burst.ps1 +36 -0
- package/scripts/shoot-screen.ps1 +7 -0
package/README.md
CHANGED
|
@@ -80,6 +80,12 @@ desktop-pet
|
|
|
80
80
|
{ "curtain": false }
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
+
开关只有**一个**生效点 —— `renderer/fx.js` 的 `FX.setDimEnabled()`:`dim()` 的调用点一半在
|
|
84
|
+
`app.js` 的演出时间线,一半散在 `awaken.js` 每套觉醒亮相时的柔暗底(`fx.dim(0.34, …, c.stroke, true)`),
|
|
85
|
+
只在调用点各自判会漏 —— 漏掉的那一处就是屏幕上那块暗紫色矩形(`capturePage` 的四角探针也看不见它,
|
|
86
|
+
因为柔暗底在画面正中浓、到四角已经淡到 0,但**窗口的四条边**上依然是有颜色的)。
|
|
87
|
+
要回归就往 `scripts/probe-dim.py` 那条命令,看环带的中位 alpha。
|
|
88
|
+
|
|
83
89
|
要不要铺暗幕,取决于壁纸:加色混合的光效(金光、雷光)在**亮色壁纸**上本来就很难拉开对比,
|
|
84
90
|
垫一层暗底才亮得起来 —— 想回到老的「黑屏切入」就把 `curtain` 改成 `true`(代价是屏幕上会多一个暗窗)。
|
|
85
91
|
关掉时光效照跑,只是没有暗底垫着。
|
|
@@ -332,6 +338,11 @@ PET_SNAPSHOT_MS=6500 npx electron . --snapshot --anim=awaken:seraph # 收招
|
|
|
332
338
|
PET_SNAPSHOT_MS=1500 PET_SNAPSHOT_OUT=f1.png npx electron . --snapshot --anim=run
|
|
333
339
|
PET_SNAPSHOT_MS=1580 PET_SNAPSHOT_OUT=f2.png npx electron . --snapshot --anim=run
|
|
334
340
|
|
|
341
|
+
# 觉醒「不该有暗色矩形」的回归测试:沿画布四边取 18px 环带看 alpha。
|
|
342
|
+
# 注意 --snapshot 的 capturePage 只证明「画布上没画暗色」,不能证明桌面上没有暗雾,
|
|
343
|
+
# 所以真正要看的是脚本里的中位 alpha(整窗覆盖 ≈ 几十,只有粒子 ≈ 0):
|
|
344
|
+
python scripts/probe-dim.py /tmp/dim-probe 2000,2400,3000,3600 seraph nocturne thunder astral
|
|
345
|
+
|
|
335
346
|
# 2) 带调试日志启动(写 pet-debug.log)
|
|
336
347
|
powershell -ExecutionPolicy Bypass -File scripts/debug-run.ps1
|
|
337
348
|
|
package/assets/outfits.json
CHANGED
|
@@ -11,9 +11,29 @@
|
|
|
11
11
|
"zoom": 1.5
|
|
12
12
|
},
|
|
13
13
|
"list": [
|
|
14
|
-
{
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
{
|
|
15
|
+
"id": "seraph",
|
|
16
|
+
"name": "圣光 · 炽天使",
|
|
17
|
+
"skill": "圣裁之光",
|
|
18
|
+
"hint": "金白光柱 / 羽翼 / 圆形符文阵"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "nocturne",
|
|
22
|
+
"name": "暗影 · 血月狂想",
|
|
23
|
+
"skill": "夜狱降临",
|
|
24
|
+
"hint": "血月升起 / 蝠影 / 黑雾"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"id": "thunder",
|
|
28
|
+
"name": "雷霆 · 天罚",
|
|
29
|
+
"skill": "雷神裁决",
|
|
30
|
+
"hint": "雷云 / 连锁闪电 / 电磁环"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "astral",
|
|
34
|
+
"name": "幻梦 · 星海",
|
|
35
|
+
"skill": "星海咏叹",
|
|
36
|
+
"hint": "星尘漩涡 / 星河光带 / 水晶碎片"
|
|
37
|
+
}
|
|
18
38
|
]
|
|
19
39
|
}
|
package/package.json
CHANGED
package/renderer/app.js
CHANGED
|
@@ -732,11 +732,11 @@ let awakenToken = 0; // 每次演出一个令牌;旧演出的挂起回调拿
|
|
|
732
732
|
|
|
733
733
|
/**
|
|
734
734
|
* 暗幕开关(assets/outfits.json 的 curtain,默认 false = 不铺暗色底)。
|
|
735
|
-
*
|
|
736
|
-
*
|
|
735
|
+
* 真正生效的位置在 fx.js 的 FX.setDimEnabled():那里是唯一的开关,
|
|
736
|
+
* 因为 dim() 的调用点除了下面这条时间线,还有 awaken.js 里每套觉醒亮相时的柔暗底。
|
|
737
|
+
* 漏掉任何一处都会在屏幕上留一块暗色矩形,所以不要在调用点反复判。
|
|
737
738
|
*/
|
|
738
739
|
const curtainOn = () => cfg.awaken?.curtain === true;
|
|
739
|
-
const curtainAlpha = (a) => (curtainOn() ? a : 0);
|
|
740
740
|
|
|
741
741
|
/** 角色当前的屏幕位置,事件在触发的那一刻现取(窗口刚被放大过) */
|
|
742
742
|
/**
|
|
@@ -810,14 +810,16 @@ async function playAwaken(id, { auto = false } = {}) {
|
|
|
810
810
|
|
|
811
811
|
const token = ++awakenToken;
|
|
812
812
|
state.autoAwaken = !!auto;
|
|
813
|
+
// 整场演出的暗幕开关一次性定好(含 awaken.js 里各套觉醒自己排的柔暗底)
|
|
814
|
+
FX.setDimEnabled(curtainOn());
|
|
813
815
|
if (state.roaming) {
|
|
814
816
|
window.pet.haltRoam();
|
|
815
817
|
endLoco('awaken');
|
|
816
818
|
}
|
|
817
819
|
holdShow();
|
|
818
820
|
|
|
819
|
-
// 1) 先把屏幕压黑(curtain=false
|
|
820
|
-
FX.dim(
|
|
821
|
+
// 1) 先把屏幕压黑(curtain=false 时 FX 内部直接忽略)
|
|
822
|
+
FX.dim(def.dimAlpha, 170, def.palette.stroke);
|
|
821
823
|
|
|
822
824
|
// 2) 暗幕盖满之后再把窗口放大,同时按新窗口高度算「镜头推近」的倍数
|
|
823
825
|
await new Promise((r) => setTimeout(r, 180));
|
|
@@ -868,8 +870,7 @@ async function playAwaken(id, { auto = false } = {}) {
|
|
|
868
870
|
|
|
869
871
|
// 蓄力:暗幕从全黑提到 0.6(soft),让玩家真的能看到蓄力动作在演,
|
|
870
872
|
// 而不是从头到尾一层黑幕摆完五秒 —— 那多帧素材就白做了。
|
|
871
|
-
|
|
872
|
-
if (sheet.charge && curtainOn()) {
|
|
873
|
+
if (sheet.charge) {
|
|
873
874
|
events.push([sheet.charge.at, () => FX.dim(0.6, 420, def.palette.stroke, true)]);
|
|
874
875
|
}
|
|
875
876
|
|
|
@@ -896,9 +897,9 @@ async function playAwaken(id, { auto = false } = {}) {
|
|
|
896
897
|
def.closeAt,
|
|
897
898
|
() => {
|
|
898
899
|
if (token !== awakenToken) return;
|
|
899
|
-
FX.dim(
|
|
900
|
-
//
|
|
901
|
-
//
|
|
900
|
+
FX.dim(def.dimAlpha, 240, def.palette.stroke);
|
|
901
|
+
// 窗口变回去的那一帧本来就不用遮(窗口只向上向两侧收回,角色不动);
|
|
902
|
+
// curtain=true 时这层仍然和以前一样盖住收招
|
|
902
903
|
setTimeout(() => {
|
|
903
904
|
if (token !== awakenToken) return;
|
|
904
905
|
el.stage.classList.remove('awakening', 'flashing', 'shaking');
|
|
@@ -1111,6 +1112,7 @@ async function init() {
|
|
|
1111
1112
|
cfg = await window.pet.getConfig();
|
|
1112
1113
|
dbgOn = !!cfg.debug;
|
|
1113
1114
|
gapOverride = cfg.roamGapMs || null;
|
|
1115
|
+
FX.setDimEnabled(cfg.awaken?.curtain === true); // 暗幕开关交给引擎,见 curtainOn 的注释
|
|
1114
1116
|
dbg(`init debug=${dbgOn} roamGap=${gapOverride ?? '默认区间'}`);
|
|
1115
1117
|
state.scale = cfg.scale ?? 1;
|
|
1116
1118
|
applyScale(state.scale);
|
package/renderer/fx.js
CHANGED
|
@@ -550,6 +550,14 @@ const FX = (() => {
|
|
|
550
550
|
let dimColor = '#0a0410';
|
|
551
551
|
let dimSoft = false;
|
|
552
552
|
|
|
553
|
+
// 暗幕总开关。setDimEnabled(false) 之后 dim() 全部变成空操作,
|
|
554
|
+
// 也就是「演出期间窗口背景完全透明,桌面上只看得到角色和光效」。
|
|
555
|
+
//
|
|
556
|
+
// 为什么开关放在引擎里,而不是让每个调用点自己判:dim 的调用点一半在 app.js 的时间线、
|
|
557
|
+
// 一半散在 awaken.js 每套觉醒的 build 里(亮相那一下的柔暗底),
|
|
558
|
+
// 漏掉任何一处就会在屏幕上留下一层「暗色矩形」—— 这正是之前的 bug。
|
|
559
|
+
let dimAllowed = true;
|
|
560
|
+
|
|
553
561
|
/**
|
|
554
562
|
* 暗幕 / 全屏闪白。alpha 0 = 全透明,1 = 全黑。
|
|
555
563
|
* 0→1 用来做「黑屏切入」,1→0 用来做「角色亮相」,alpha 1 + 白色 = 闪白。
|
|
@@ -558,6 +566,13 @@ const FX = (() => {
|
|
|
558
566
|
* 本来就很难看清楚(白底上加白还是白),垫一层柔和的暗底,金光/雷光才能真的亮起来。
|
|
559
567
|
*/
|
|
560
568
|
function dim(alpha, ms, color, soft) {
|
|
569
|
+
if (!dimAllowed) {
|
|
570
|
+
// 关掉时顺手把残留清干净,避免开关切换的瞬间卡在半透明
|
|
571
|
+
dimNow = 0;
|
|
572
|
+
dimTo = 0;
|
|
573
|
+
dimSoft = false;
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
561
576
|
if (color) dimColor = color;
|
|
562
577
|
if (soft !== undefined) dimSoft = !!soft;
|
|
563
578
|
const target = Math.max(0, Math.min(1, alpha));
|
|
@@ -1023,6 +1038,19 @@ const FX = (() => {
|
|
|
1023
1038
|
play,
|
|
1024
1039
|
stop,
|
|
1025
1040
|
reset,
|
|
1041
|
+
/** 暗幕总开关:false = 演出全程不铺任何暗色底(默认关,见 app.js 的 curtainOn) */
|
|
1042
|
+
setDimEnabled(on) {
|
|
1043
|
+
dimAllowed = !!on;
|
|
1044
|
+
if (!dimAllowed) {
|
|
1045
|
+
dimNow = 0;
|
|
1046
|
+
dimTo = 0;
|
|
1047
|
+
dimSoft = false;
|
|
1048
|
+
drawDim(); // dimNow 已归零 → 内部直接 return,等效于不再画
|
|
1049
|
+
}
|
|
1050
|
+
},
|
|
1051
|
+
get dimEnabled() {
|
|
1052
|
+
return dimAllowed;
|
|
1053
|
+
},
|
|
1026
1054
|
get busy() {
|
|
1027
1055
|
return (
|
|
1028
1056
|
!!script || emitters.length > 0 || geo.length > 0 || dimNow !== dimTo || aliveParticles() > 0
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# QA:用 capturePage 逐帧探「整窗覆盖物」——沿画布四边 18px 环带取 alpha
|
|
3
|
+
# 用法: probe-dim.py <输出目录> <a,b,c... 时刻ms> <anim...>
|
|
4
|
+
import os, subprocess, sys, glob
|
|
5
|
+
from PIL import Image
|
|
6
|
+
|
|
7
|
+
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
8
|
+
outdir, stamps, anims = sys.argv[1], [int(v) for v in sys.argv[2].split(',')], sys.argv[3:]
|
|
9
|
+
os.makedirs(outdir, exist_ok=True)
|
|
10
|
+
|
|
11
|
+
def ring_stats(path):
|
|
12
|
+
im = Image.open(path).convert('RGBA')
|
|
13
|
+
W, H = im.size
|
|
14
|
+
px = im.load()
|
|
15
|
+
band = 18
|
|
16
|
+
alphas = []
|
|
17
|
+
for x in range(0, W, 3):
|
|
18
|
+
for y in list(range(0, band)) + list(range(H - band, H)):
|
|
19
|
+
alphas.append(px[x, y][3])
|
|
20
|
+
for y in range(0, H, 3):
|
|
21
|
+
for x in list(range(0, band)) + list(range(W - band, W)):
|
|
22
|
+
alphas.append(px[x, y][3])
|
|
23
|
+
alphas.sort()
|
|
24
|
+
return W, H, max(alphas), alphas[len(alphas) // 2], sum(alphas) / len(alphas)
|
|
25
|
+
|
|
26
|
+
print(f"{'套装':<10}{'时刻ms':<8}{'画布':<12}{'环带max α':<11}{'环带中位 α':<11}{'环带均值 α'}")
|
|
27
|
+
bad = []
|
|
28
|
+
for anim in anims:
|
|
29
|
+
for ms in stamps:
|
|
30
|
+
dst = os.path.join(outdir, f'{anim}-{ms}.png')
|
|
31
|
+
env = dict(os.environ, PET_SNAPSHOT_OUT=dst, PET_SNAPSHOT_MS=str(ms))
|
|
32
|
+
subprocess.run(['node', 'bin/desktop-pet.js', '--snapshot', f'--anim=awaken:{anim}'],
|
|
33
|
+
cwd=ROOT, env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
34
|
+
W, H, mx, med, avg = ring_stats(dst)
|
|
35
|
+
flag = ''
|
|
36
|
+
if mx > 12:
|
|
37
|
+
flag = ' ← 整窗有覆盖物!'
|
|
38
|
+
bad.append((anim, ms, mx))
|
|
39
|
+
print(f"{anim:<10}{ms:<8}{f'{W}x{H}':<12}{mx:<11}{med:<11}{avg:<8.1f}{flag}")
|
|
40
|
+
print()
|
|
41
|
+
print("结论:", "环带上还有非零 alpha 的帧:" + str(bad) if bad else "全部帧环带 alpha 均为 0 —— 没有任何整窗覆盖物")
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# QA:在「已确认是亮色」的背景板上,对同一段觉醒演出连拍整屏。
|
|
3
|
+
# 用法: qa-awaken-capture.sh <输出目录> [curtain:false|true]
|
|
4
|
+
set -u
|
|
5
|
+
# 仓库根 = 本脚本所在目录的上一级,不写死绝对路径
|
|
6
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -W 2>/dev/null || cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
7
|
+
OUT="$1"; CURTAIN="${2:-false}"; ANIM="${3:-awaken:seraph}"
|
|
8
|
+
mkdir -p "$OUT"
|
|
9
|
+
|
|
10
|
+
kill_all() {
|
|
11
|
+
powershell -NoProfile -Command "Get-Process electron -ErrorAction SilentlyContinue | Stop-Process -Force" >/dev/null 2>&1
|
|
12
|
+
powershell -NoProfile -Command "Get-Process powershell -ErrorAction SilentlyContinue | Where-Object { \$_.MainWindowTitle -eq '' } | Stop-Process -Force" >/dev/null 2>&1
|
|
13
|
+
sleep 1
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
# 1) 背景板,等到「照片确实是亮的」再继续
|
|
17
|
+
kill_all
|
|
18
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File "$ROOT/scripts/qa-backdrop.ps1" \
|
|
19
|
+
"$(cygpath -w $ROOT/outputs/qa-backdrop.png)" >/dev/null 2>&1 &
|
|
20
|
+
for i in $(seq 1 25); do
|
|
21
|
+
sleep 1
|
|
22
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File "$ROOT/scripts/shoot-screen.ps1" "$(cygpath -w $OUT/warmup.png)" >/dev/null 2>&1
|
|
23
|
+
bright=$(python - "$OUT/warmup.png" <<'PY'
|
|
24
|
+
import sys
|
|
25
|
+
from PIL import Image
|
|
26
|
+
im = Image.open(sys.argv[1]).convert('L').resize((32,18))
|
|
27
|
+
px = sorted(im.getdata()); print(px[len(px)//2])
|
|
28
|
+
PY
|
|
29
|
+
)
|
|
30
|
+
echo "背景板预热 ${i}s 中位亮度=$bright"
|
|
31
|
+
[ "$bright" -gt 200 ] && break
|
|
32
|
+
done
|
|
33
|
+
|
|
34
|
+
# 2) 开桌宠(curtain 由调用方先改好 outfits.json)
|
|
35
|
+
cd "$ROOT"
|
|
36
|
+
PET_DEBUG=1 nohup node bin/desktop-pet.js --no-auto > "$OUT/pet.out" 2>&1 &
|
|
37
|
+
ok=0
|
|
38
|
+
for i in $(seq 1 25); do
|
|
39
|
+
sleep 1
|
|
40
|
+
if curl -s --max-time 2 http://127.0.0.1:8520/health > "$OUT/health.json"; then ok=1; break; fi
|
|
41
|
+
done
|
|
42
|
+
if [ "$ok" != 1 ]; then
|
|
43
|
+
echo "!! /health 起不来,pet.out:"; cat "$OUT/pet.out"; tail -3 "$OUT/pet-debug.log" 2>/dev/null; exit 1
|
|
44
|
+
fi
|
|
45
|
+
echo "pet 就绪(${i}s)"
|
|
46
|
+
grep -o '"bounds":{[^}]*}' "$OUT/health.json" || true
|
|
47
|
+
|
|
48
|
+
# 3) 连拍整屏,中途触发一次觉醒
|
|
49
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File "$ROOT/scripts/qa-burst.ps1" \
|
|
50
|
+
"$(cygpath -w $OUT)" 6.5 2.0 "$ANIM"
|
|
51
|
+
|
|
52
|
+
# 门禁:base 必须是亮的,否则背景板没真的在最上层,这轮数据不能用
|
|
53
|
+
base_med=$(python - "$OUT/base.png" <<'PY2'
|
|
54
|
+
import sys
|
|
55
|
+
from PIL import Image
|
|
56
|
+
im = Image.open(sys.argv[1]).convert('L').resize((60,34))
|
|
57
|
+
px = sorted(im.getdata()); print(px[len(px)//2])
|
|
58
|
+
PY2
|
|
59
|
+
)
|
|
60
|
+
echo "base.png 全屏中位亮度=$base_med"
|
|
61
|
+
if [ "$base_med" -lt 180 ]; then
|
|
62
|
+
echo "!! 背景板被盖住了(base 偏暗),这轮作废"; exit 2
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
cp "$ROOT/pet-debug.log" "$OUT/pet-debug.log" 2>/dev/null
|
|
66
|
+
grep -o 'awaken-open {[^}]*}' "$OUT/pet-debug.log" | tail -1 > "$OUT/awaken-rect.txt"
|
|
67
|
+
echo "curtain=$CURTAIN → $OUT ($(ls "$OUT"/t*.png | wc -l) 帧) 觉醒矩形: $(cat "$OUT/awaken-rect.txt")"
|
|
68
|
+
kill_all
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
|
|
2
|
+
$img = [System.Drawing.Image]::FromFile($args[0])
|
|
3
|
+
$f = New-Object System.Windows.Forms.Form
|
|
4
|
+
$f.FormBorderStyle = 'None'
|
|
5
|
+
$f.StartPosition = 'Manual'
|
|
6
|
+
$f.Location = New-Object System.Drawing.Point(0, 0)
|
|
7
|
+
$f.Size = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Size
|
|
8
|
+
$f.BackColor = [System.Drawing.Color]::White
|
|
9
|
+
$f.BackgroundImage = $img
|
|
10
|
+
$f.BackgroundImageLayout = 'Stretch'
|
|
11
|
+
$f.ShowInTaskbar = $false
|
|
12
|
+
$f.TopMost = $true
|
|
13
|
+
$f.Add_Shown({ $f.Activate() })
|
|
14
|
+
[System.Windows.Forms.Application]::Run($f)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# QA: capture the whole screen on a fixed cadence and fire one awaken mid-way.
|
|
2
|
+
# ASCII only on purpose: PowerShell 5.1 reads .ps1 as ANSI, CJK comments can break parsing.
|
|
3
|
+
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
|
|
4
|
+
|
|
5
|
+
$outDir = $args[0]
|
|
6
|
+
$seconds = [double]$args[1]
|
|
7
|
+
$delay = [double]$args[2] # wait before firing the awaken
|
|
8
|
+
$anim = $args[3]
|
|
9
|
+
|
|
10
|
+
$screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
|
|
11
|
+
$sw = [System.Diagnostics.Stopwatch]::StartNew()
|
|
12
|
+
|
|
13
|
+
function Shoot([string]$name) {
|
|
14
|
+
$bmp = New-Object System.Drawing.Bitmap($screen.Width, $screen.Height)
|
|
15
|
+
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
16
|
+
$g.CopyFromScreen($screen.Location, [System.Drawing.Point]::Empty, $screen.Size)
|
|
17
|
+
$bmp.Save((Join-Path $outDir $name), [System.Drawing.Imaging.ImageFormat]::Png)
|
|
18
|
+
$g.Dispose(); $bmp.Dispose()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
Start-Sleep -Seconds $delay
|
|
22
|
+
Shoot 'base.png'
|
|
23
|
+
$t0 = $sw.Elapsed.TotalMilliseconds
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
Invoke-RestMethod -Method Post -Uri 'http://127.0.0.1:8520/action' `
|
|
27
|
+
-ContentType 'application/json' -Body ("{`"name`":`"$anim`"}") | Out-Null
|
|
28
|
+
} catch { Write-Output "trigger failed: $_" }
|
|
29
|
+
|
|
30
|
+
$i = 0
|
|
31
|
+
while ($sw.Elapsed.TotalSeconds -lt ($delay + $seconds)) {
|
|
32
|
+
$ms = [int]($sw.Elapsed.TotalMilliseconds - $t0)
|
|
33
|
+
Shoot ('t{0:d5}.png' -f $ms)
|
|
34
|
+
$i++
|
|
35
|
+
}
|
|
36
|
+
Write-Output "shots=$i dur=$([int]($sw.Elapsed.TotalSeconds - $delay))s"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
|
|
2
|
+
$b = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
|
|
3
|
+
$bmp = New-Object System.Drawing.Bitmap($b.Width, $b.Height)
|
|
4
|
+
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
5
|
+
$g.CopyFromScreen($b.Location, [System.Drawing.Point]::Empty, $b.Size)
|
|
6
|
+
$bmp.Save($args[0], [System.Drawing.Imaging.ImageFormat]::Png)
|
|
7
|
+
$g.Dispose(); $bmp.Dispose()
|