Windows批处理文件街机游戏的优化

时间:2013-11-30 23:05:06

标签: windows batch-file optimization

我制作了一个批处理文件游戏,它可以正常运行,但它不稳定且难看。我已经了解了线程,但我不想在我的第一个版本中实现它。在我开始用这个游戏做更高级的事情之前,我希望能够优化。我的问题是:我可以对这个游戏进行哪些优化,以便它不会变得不稳定,而且在显示屏上不会那么烦人。欢迎任何有关如何使其更快更清晰或占用更少内存的想法或意见,但是,请不要发布如下答案:“不要使用批处理”“重写它(在这里插入语言)”“用vb做这部分-script“等...因为他们没有帮助,他们也没有回答这个问题。任何和所有非批评的仇恨批评都受到欢迎。

这是代码:

@setlocal enableextensions enabledelayedexpansion
@echo off
color 0a
mode con lines=35 cols=50
cls
set instructions=use a and d to move left and right, w to fire. use q to quit and p to pause.
set height=30
set length=         
set screen=50
set swidth=20
set amo=8
set lives=3
set 1=0
set 2=1
set 3=2
set 4=3
set 5=4
set 6=0
set 7=1
set 8=2
set 9=3
set 10=4

echo. What quality would you like?
echo.   1. fast, but the graphics suck!
echo.   2. medium both ways.
echo.   3. slow, but the graphics are better!
choice /n /c:123
set firequal=%errorlevel%00
cls
echo %instructions%
echo.
pause
cls

::main
:controls
cls
if %height% EQU 2 goto gameover
if %lives% LSS 1 goto gameover
cls
set /a shouldbomb+=1
set /a whenbomb=shouldbomb%%15
if %whenbomb% == 9 call :bomb
if '%ret%'=='1' exit /b
set ret=
cls
set alive=
for /l %%i in (1,1,10) do if defined %%i set alive=true
if not defined alive goto win
cls
for /l %%i in (1,1,5) do (
      if defined %%i (
     set /p a=[_]  <nul
      ) else (
     set /p a=...  <nul
      )
)
echo.
for /l %%i in (6,1,10) do (
      if defined %%i (
     set /p a=[_]  <nul
      ) else (
     set /p a=...  <nul
      )
)
for /l %%a in (1,1,%height%) do echo.
echo %length%[]
echo.
for /l %%i in (1,1,%amo%) do set /p a=^|<nul
echo.
choice /c adwqp0 /n /t 1 /d 0
if %errorlevel% equ 1 goto :left
if %errorlevel% equ 2 goto :right
if %errorlevel% equ 3 goto :fire
if %errorlevel% equ 4 (cls&exit /b)
if %errorlevel% equ 5 pause&goto controls
if %errorlevel% equ 6 goto :inactive
goto controls

::move player left
:left
if '!length!' NEQ '' set length=!length:~0,-1!
goto controls

::move player right
:right
call :strlen shiplen length
if %shiplen% GTR %swidth% goto controls
set length=%length% 
goto controls

::fire a shot upwards
:fire
if '!amo!' LSS '1' goto controls
cls
set /a amo-=1
for /l %%i in (%height%,-1,2) do (
   cls
   for /l %%i in (1,1,5) do (
     if defined %%i (
        set /p a=[_]  <nul
     ) else (
        set /p a=...  <nul
     )
   )
   echo.
   for /l %%i in (6,1,10) do (
     if defined %%i (
        set /p a=[_]  <nul
     ) else (
        set /p a=...  <nul
     )
   )
   for /l %%j in (1,1,%%i) do echo.
   echo %length% ^
   set /a ship=height-%%i-1
   for /l %%b in (1,1,!ship!) do echo.
   echo %length%[]
   echo.
   for /l %%i in (1,1,%amo%) do set /p a=^|<nul
   echo.
   for /l %%a in (1,1,%firequal%) do call >nul 2>&1
)
call :checkshot
set /a shouldbomb+=1
set /a whenbomb=shouldbomb%%2
if %whenbomb% == 0 call :bomb
goto controls

:inactive
if %amo% LSS 10 set /a amo+=1
if !height! NEQ 2 set /a height-=1
call :bomb
goto controls

:bomb
:btop
set bombx=
for /l %%a in (1,1,10) do (
   if defined %%a (
     set /a randomnum=%random%%%5
     if '!%%a!'=='%randomnum%' (
        set /a "bombx=5*(!%%a!)"
     )
      )
   )
)
if not defined bombx goto btop
cls
set bomb= 
for /l %%b in (1,1,!bombx!) do (
      set bomb=!bomb! 
)
set /a bombh=height-1
for /l %%c in (1,1,!bombh!) do (
   cls
   for /l %%i in (1,1,5) do (
     if defined %%i (
        set /p a=[_]  <nul
     ) else (
        set /p a=...  <nul
     )
   )
   echo.
   for /l %%i in (6,1,10) do (
     if defined %%i (
        set /p a=[_]  <nul
     ) else (
        set /p a=...  <nul
     )
   )
   for /l %%b in (1,1,%%c) do echo.
   echo !bomb!x
   set /a ship=height-%%c-1
   for /l %%b in (1,1,!ship!) do echo.
   echo %length%[]
   echo.
   for /l %%i in (1,1,%amo%) do set /p a=^|<nul
   echo.
   for /l %%a in (1,1,%firequal%) do call >nul 2>&1
)
if "%bomb%"   == "%length%"   call :looselife
if "%bomb% "  == "%length%"   call :looselife
if "%bomb%"   == "%length% "  call :looselife
if "%bomb%  " == "%length%"   call :looselife
if "%bomb%"   == "%length%  " call :looselife
exit /b

:strlen <resultVar> <stringVar>
(   
    setlocal EnableDelayedExpansion
    set "s=!%~2!#"
    set "len=0"
    for %%P in (1024 512 256 128 64 32 16 8 4 2 1) do (
    if "!s:~%%P,1!" NEQ "" ( 
        set /a "len+=%%P"
        set "s=!s:~%%P!"
    )
    )
)
( 
    endlocal
    set "%~1=%len%"
    exit /b
)

:checkshot
call :strlen slen length
for /l %%i in (0,5,20) do (
   if '!slen!' == '%%i' (
      set /a hit=%%i
      set /a hit=hit/5+1
      set /a hit2=hit+5
      if not defined !hit2! set !hit!=
      if defined !hit2! set !hit2!=
   )
)
exit /b

:looselife
set /a lives-=1
set length=         
set 1=0
set 2=1
set 3=2
set 4=3
set 5=4
if %lives% GTR 1 timeout /nobreak 1 >nul 2>&1
exit /b

:win
cls
echo YOU WIN^!^!^!^!
echo.
echo GOOD JOB^!^!^!
echo.
pause
cls
exit /b

:gameover
cls
echo YOU LOOSE.
echo.
echo PLEASE TRY AGAIN.
echo.
pause
cls
set ret=1

提前感谢您的帮助。

P.S。我正在写这个游戏来说服朋友学习除了HTML以外的东西,虽然批处理不是最好的,但他使用的是Windows,他现在只会做一些简单的事情。他十二岁,所以我觉得批次是最好的选择。

2 个答案:

答案 0 :(得分:5)

你绝对可以大大改善。我知道,因为我已经制作了一个非常流畅且可玩的SNAKE using pure Windows batch版本!尝试一下 - 我想你会对那些发霉的旧批次能做的事感到惊讶和印象: - )

当然链接有代码,但它也有一些关于我用来使其表现如此出色的技术的指针。仔细阅读整个第一篇文章,并阅读其余部分内容以了解其他一些重要进展。

优化是一个很大的主题。我将简单地总结一下,而不是重复这里的所有信息。请点击链接了解更多详情。

1)最小化GOTO和CALL语句。

  • 对于传统批量函数调用的主要速度改进,我们在DosTips开发了batch macros with arguments。第一个宏观链接产生了许多重要概念。但是,我在游戏中实际使用的宏形式使用了更多elegant solution with arguments appended

  • GOTO循环可以由在新进程中运行的无限FOR循环替换。您可以通过退出子进程来摆脱循环。

2)以非阻塞的方式大大改善按键检测。

  • 批量的一个主要限制是无法在不阻碍游戏进度的情况下轻松检测按键。可以通过使用两个进程来解决问题,这两个进程都在同一个控制台窗口中运行。控制器进程读取按键并通过文本文件将它们发送到主游戏进程。控制器具有多种操作模式。游戏过程通过另一个文本文件向控制器发送命令。这种技术需要仔细协调输入和输出重定向。

  • XP上没有CHOICE命令。 DosTips的一些人发现了如何使用XCOPY来模拟CHOICE的大部分功能,它适用于所有版本的Windows。很酷!

3)屏幕画

  • 逐个字符构建屏幕非常慢。使用具有固定长度的字符串“数组”构建初始屏幕要快得多。字符串中的每个字符代表一个“像素”。字符串中的位置表示X坐标,字符串行号表示Y坐标。通常,对于任何给定的屏幕刷新,只有少数像素发生变化。可以通过使用具有简单子串操作的SET来“绘制”像素。然后可以使用CLS快速刷新整个屏幕,然后使用屏幕阵列中每行的ECHO。

4)平滑动画

  • 执行游戏逻辑和屏幕绘图所需的工作量可能会因当前游戏背景而有很大差异。但是你希望动画流畅。您可以改为测量自上次更新屏幕以来的时间,而不是在每轮运动之间有固定的延迟。只有在预定的时间过去后才会继续。只要所有游戏逻辑和绘图都可以在延迟时间段内发生,那么动画将始终是平滑的。

这是描述时序逻辑的伪代码:

initialize delayTime
initialize previousTime
loop (
  get currentTime
  set diffTime = currentTime - previousTime
  if diffTime >= delayTime (
    set previousTime = currentTime
    perform user input, game logic, and screen refresh
  )
)

这是实际的代码,用于计算自上次移动以来经过的时间。 currentTime(t2)从午夜开始测量为厘秒(1/100秒)。它使用FOR / F和基本数学进行解析和计算。如果previousTime(tDiff)在午夜之前且currentTime(t1)在午夜之后,则diffTime(t2)将为负。如果为负数,则将1天添加到diffTime以获得正确的时间间隔。

%=== compute time since last move ===%
for /f "tokens=1-4 delims=:.," %%a in ("!time: =0!") do set /a "t2=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100, tDiff=t2-t1"
if !tDiff! lss 0 set /a tDiff+=24*60*60*100

还有更多可以讨论的内容。试试SNAKE.BAT游戏,研究帖子和代码,看看你的想象力可以带你去哪里。

答案 1 :(得分:5)

一些优化

1)最好使用好的变量名。

12 ... 10这样的名字真的很糟糕,没有人知道它们有什么用处,即使你自己也不会在一个月内记住。
然后它也是一个坏主意,因为它可能有很多副作用,使用以数字开头的变量,批量存在许多只会失败的地方。

2)您应该在输出之前将输出组合成完整的线条 那么您不需要set/p echo而且速度更快。

3)应该避免对:strlen之类的函数的调用,根本不需要调用,在你的情况下,应该可以在没有strlen的情况下解决相同的问题。

4)函数:checkshot不需要for循环 我不明白你试图在那里做什么,但你测试slen,如果它是多余的5。 这可以通过

解决
set /a remainder=slen %% 5
if !remainder! EQU 0 (
  ...

5)按照dbenham的提示: - )