查看: 2365|回复: 2

[原创发布] 俄罗斯方块游戏

[复制链接]
梦石
0
星屑
2821
在线时间
585 小时
注册时间
2022-7-13
回帖
81
发表于 2023-8-18 09:30:09 | 显示全部楼层 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有账号?注册会员

×
本帖最后由 我为鱼肉 于 2023-8-20 10:26 编辑


原理简单,操作变量,有注释。可自定义任意不规则形状,有落点“影子”功能。
俄罗斯方块.zip (1.12 MB, 下载次数: 31)
[fold=代码][pre lang="ruby"]module RPG::Cache
  def self.tetris(filename, hue = 0)
    load_bitmap("Graphics/Tetris/", filename, hue)
  end
end
#==============================================================================
# ** Tetris俄罗斯方块游戏
#==============================================================================
class Tetris
  #自定义小块的样式/形状/颜色(文件名,使用RPG::Cache.tetris读取)
  Element = ["blue", "cyan", "green", "purple", "red", "yellow"]
  Element.insert(0, "gray")#影子块
  #读取数据
  Structure = load_data("Data/Tetris/Structure.rxdata")
  Ushape = load_data("Data/Tetris/Ushape.rxdata")
  Right = load_data("Data/Tetris/Right.rxdata")
  Left = load_data("Data/Tetris/Left.rxdata")
=begin
  #以下展示这些数据的生成过程,方便自定义结构(可以是任意的)与快速生成数据。
  #自定义方块结构。一个""代表某一行的数据,0空 1实
  input = []
  input[0] = ["11", "11"]#〇
  input[1] = ["1111"]#I
  input[2] = ["1", "1", "1", "1"]
  input[3] = ["011", "110"]#S
  input[4] = ["10", "11", "01"]
  input[5] = ["110", "011"]#Z
  input[6] = ["01", "11", "10"]
  input[7] = ["010", "111"]#T
  input[8] = ["10", "11", "10"]
  input[9] = ["111", "010"]
  input[10] = ["01", "11", "01"]
  input[11] = ["10", "10", "11"]#L
  input[12] = ["111", "100"]
  input[13] = ["11", "01", "01"]
  input[14] = ["001", "111"]
  input[15] = ["01", "01", "11"]#J
  input[16] = ["100", "111"]
  input[17] = ["11", "10", "10"]
  input[18] = ["111", "001"]
  #生成方块结构数据。二位数组 0空 1实
  Structure = []
  for i in 0...input.size
    data = Table.new(input[0].size, input.size)
    for y in 0...data.ysize
      for x in 0...data.xsize
        data[x, y] = input[y][x].chr.to_i
      end
    end
    Structure = data
  end
  save_data(Structure, "Data/Tetris/Structure.rxdata")
  #自定义旋转类型。"Right[1] = 2"表示1号方块顺时针旋转为2号方块,Left逆时针同理
  Right = []
  Right[1] = 2 ; Right[2] = 1
  Right[3] = 4 ; Right[4] = 3
  Right[5] = 6 ; Right[6] = 5
  Right[7] = 8 ; Right[8] = 9 ; Right[9] = 10 ; Right[10] = 7
  Right[11] = 12 ; Right[12] = 13 ; Right[13] = 14 ; Right[14] = 11
  Right[15] = 16 ; Right[16] = 17 ; Right[17] = 18 ; Right[18] = 15
  Left = []
  Left[1] = 2 ; Left[2] = 1
  Left[3] = 4 ; Left[4] = 3
  Left[5] = 6 ; Left[6] = 5
  Left[7] = 10 ; Left[8] = 7 ; Left[9] = 8 ; Left[10] = 9
  Left[15] = 18 ; Left[16] = 15 ; Left[17] = 16 ; Left[18] = 17
  save_data(Right, "Data/Tetris/Right.rxdata")
  save_data(Left, "Data/Tetris/Left.rxdata")
  #生成“U形”数据(从左、右、下方看第一个不为空的坐标,方便做移动判定)
  #这里不需要自定义,只需要设置好input/Structure,代码自动生成。
  Ushape = []
  for i in 0...Structure.size
    left = {}
    right = {}
    down = {}
    for y in 0...Structure.ysize
      for x in 0...Structure.xsize
        if Structure[x, y] > 0 and left[y].nil?
          left[y] = [x, y]
        end
        rx = Structure.xsize - 1 - x
        if Structure[rx, y] > 0 and right[y].nil?
          right[y] = [rx, y]
        end
      end
    end
    for x in 0...Structure.xsize
      for y in 0...Structure.ysize
        dy = Structure.ysize - 1 - y
        if Structure[x, dy] > 0 and down[x].nil?
          down[x] = [x, dy]
        end
      end
    end
    Ushape = [left.values, right.values, down.values]
  end
  save_data(Ushape, "Data/Tetris/Ushape.rxdata")
=end
  #--------------------------------------------------------------------------
  # * initialize初始化
  # task  分数目标,>=目标胜利
  # level 难度(帧数),数字越大砖块下落越慢
  #--------------------------------------------------------------------------
  def initialize(task, level)
    @drawn = Table.new(10, 20)#生成一个宽度10高度20的画板
    @redraw = Table.new(10, 20)
    reset_active
    @score = 0
    @count = 0
    @task = task
    @level = level
    @rect = Rect.new(0, 0, 23, 23)#每个小块23*23与Element对应
    @clear = Color.new(0, 0, 0, 0)
  end
  #--------------------------------------------------------------------------
  # * reset_active重设方块
  # f 初始化数据??? false初始化 true按照@following继续再随机一个@following
  #--------------------------------------------------------------------------
  def reset_active(f = false)
    @active = f ? @following : Structure[rand(Structure.size)]
    @active_element = f ? @following_element : 1 + rand(Element.size - 1)
    @x = (10 - @active.xsize) / 2
    @y = -@active.ysize
    @following = Structure[rand(Structure.size)]
    @following_element = 1 + rand(Element.size - 1)
    make_highest#生成当前条件下@active最大Y坐标
  end
  #--------------------------------------------------------------------------
  # * redraw_follower重绘预告框
  #--------------------------------------------------------------------------
  def redraw_follower
    @follower.bitmap.fill_rect(0, 0, 230, 124, @clear)#相当于.clear
    bitmap = RPG::Cache.tetris(Element[@following_element])
    x = (230 - 23 * @following.xsize) / 2#保证始终展示在bitmap中央
    y = (124 - 23 * @following.ysize) / 2
    for c in 0...@following.xsize
      for r in 0...@following.ysize
        next if @following[c, r] == 0
        @follower.bitmap.blt(x + c * 23, y + r * 23, bitmap, @rect)
      end
    end
  end
  #--------------------------------------------------------------------------
  # * redraw_degree重绘难度牌 分数越高难度越高???
  #--------------------------------------------------------------------------
  def redraw_degree
    @degree.bitmap.fill_rect(0, 0, 198, 22, @clear)
    @degree.bitmap.draw_text(0, 0, 198, 22, @level.to_s, 2)
  end
  #--------------------------------------------------------------------------
  # * redraw_scorer重绘计分器
  #--------------------------------------------------------------------------
  def redraw_scorer
    @scorer.bitmap.fill_rect(0, 0, 198, 22, @clear)
    @scorer.bitmap.draw_text(0, 0, 198, 22, @score.to_s, 2)
  end
  #--------------------------------------------------------------------------
  # * adder叠加器(@drawn和@active的叠加),用以判断(x, y)是否需要重绘
  #--------------------------------------------------------------------------
  def adder(x, y)
    if @drawn[x, y] > 0
      return @drawn[x, y]
    end
    if x >= @x and
      x <= @x + @active.xsize - 1 and
      y >= @y and
      y <= @y + @active.ysize - 1
      if @active[x - @x, y - @y] > 0
        return @active_element
      end
    else
      if @shadow and x >= @x and
        x <= @x + @active.xsize - 1 and
        y >= @highest and
        y <= @highest + @active.ysize - 1
        return 99 if @active[x - @x, y - @highest] > 0#影子
      end
    end
    return 0
  end
  #--------------------------------------------------------------------------
  # * redraw_board自重绘 这个方法或许有些多余了,只需改变@drawn时直接重绘即可
  #--------------------------------------------------------------------------
  def redraw_board
    for x in 0..9
      for y in 0..19
        value = adder(x, y)
        if @redraw[x, y] != value
          @redraw[x, y] = value
          if @redraw[x, y] == 0
            @board.bitmap.fill_rect(23 * x, 23 * y, 23, 23, @clear)
          elsif @redraw[x, y] == 99#影子
            bitmap = RPG::Cache.tetris(Element[0])
            @board.bitmap.blt(x * 23, y * 23, bitmap, @rect)
          else
            bitmap = RPG::Cache.tetris(Element[@redraw[x, y]])
            @board.bitmap.blt(x * 23, y * 23, bitmap, @rect)
          end
        end
      end
    end
  end
  #--------------------------------------------------------------------------
  # * earn得分lines消除了几行
  #得分=行数*(30+(100-难度)/10)
  #--------------------------------------------------------------------------
  def earn(lines)
    @score += lines * (30 + (100 - @level) / 10)
  end
  #--------------------------------------------------------------------------
  # * victory游戏胜利
  #--------------------------------------------------------------------------
  def victory
    print "胜利!"
    $scene = Scene_Map.new
  end
  #--------------------------------------------------------------------------
  # * defeat游戏失败
  #--------------------------------------------------------------------------
  def defeat
    print "失败!"
    $scene = Scene_Map.new
  end
  #--------------------------------------------------------------------------
  # * 消除判断
  #--------------------------------------------------------------------------
  def judge
    #取得最小y和消除了多少行
    min = -1
    lines = 0
    for y in @y..19
      drawn = 100
      for x in 0..9
        drawn = @drawn[x, y] if @drawn[x, y] < drawn
      end
      if drawn > 0
        min = y if min == -1
        lines += 1
      end
    end
    #交换数据
    for r in 0..min - 1
      y = min - 1 - r
      for c in 0..9
        @drawn[c, y + lines] = @drawn[c, y]
      end
    end
    #其他处理
    earn(lines)
    redraw_scorer
    if @score >= @task
      victory
    end
  end
  #--------------------------------------------------------------------------
  # * 移动碰撞检测
  # direction方向 0左 1右 2下
  # step步数 仅在make_highest中使用
  #--------------------------------------------------------------------------
  def move_collision(direction, step = 1)
    u = Ushape[Structure.index(@active)][direction]
    add_x = add_y = 0
    case direction
    when 0
      add_x = -1
    when 1
      add_x = 1
    when 2
      add_y = step
    end
    for i in u
      x = @x + i[0] + add_x
      y = @y + i[1] + add_y
      next if y < 0
      if y > 19 or @drawn[x, y] > 0
        return true
      end
    end
    return false
  end
  #--------------------------------------------------------------------------
  # * move_left左移动
  #--------------------------------------------------------------------------
  def move_left
    return if @x == 0 or move_collision(0)
    @x -= 1
    make_highest
  end
  #--------------------------------------------------------------------------
  # * move_right右移动
  #--------------------------------------------------------------------------
  def move_right
    return if @x > 9 - @active.xsize or move_collision(1)
    @x += 1
    make_highest
  end
  #--------------------------------------------------------------------------
  # * make_highest生成当前状态下@active最大Y坐标
  #--------------------------------------------------------------------------
  def make_highest
    for s in 1..22
      if move_collision(2, s)
        @highest = @y + s - 1
        return
      end
    end
  end
  #--------------------------------------------------------------------------
  # * move_down下移动
  #--------------------------------------------------------------------------
  def move_down
    if @y == @highest
      #碰撞的情况下@y坐标小于0,游戏失败
      if @y < 0
        defeat
        return
      end
      #把@active数据保存到@drawn
      for c in 0...@active.xsize
        for r in 0...@active.ysize
          if @active[c, r] > 0
            @drawn[@x + c, @y + r] = @active_element
          end
        end
      end
      #消除判断
      judge
      #重置当前方块与预告方块
      reset_active(true)
      redraw_follower
      @count = 0
      return
    end
    @y += 1
  end
  #--------------------------------------------------------------------------
  # * fall自下落
  #--------------------------------------------------------------------------
  def fall
    @count += 1
    if @count == @level
      move_down#向下移动
      @count = 0
    end
  end
  #--------------------------------------------------------------------------
  # * 旋转,direction0顺时针1逆时针
  #--------------------------------------------------------------------------
  def rotate(direction)
    index = direction == 0 ? Right[Structure.index(@active)] : Left[Structure.index(@active)]
    return if index.nil?
    #旋转后调整X,Y坐标???
    new_x = @x
    new_y = @y
    #坐标校正
    if new_x < 0
      new_x = 0
    end
    if new_x > 10 - @active.xsize
      new_x = 10 - @active.xsize
    end
    if new_y > 20 - @active.ysize
      new_y = 20 - @active.ysize
    end
    #碰撞检测
    for c in 0...Structure[index].xsize
      for r in 0...Structure[index].ysize
        x = new_x + c
        y = new_y + r
        next if y < 0
        if x < 0 or x > 9 or y > 19 or @drawn[x, y] > 0
          return
        end
      end
    end
    #执行旋转
    @x = new_x
    @y = new_y
    @active = Structure[index]
    make_highest
  end
  #--------------------------------------------------------------------------
  # * keyboard按键操作
  #--------------------------------------------------------------------------
  def keyboard
    if Input.repeat?(Input::LEFT)
      $game_system.se_play($data_system.cursor_se)
      move_left
    end
    if Input.repeat?(Input::RIGHT)
      $game_system.se_play($data_system.cursor_se)
      move_right
    end
    if Input.repeat?(Input::DOWN)
      $game_system.se_play($data_system.cursor_se)
      move_down
      @count = 0
    end
    if Input.repeat?(Input::UP)
      $game_system.se_play($data_system.decision_se)
      if @clockwise
        rotate(0)
      else
        rotate(1)
      end
    end
    if Input.trigger?(Input::B)
      $game_system.se_play($data_system.cancel_se)
      @commands.index = 0
      @commands.active = true
      @commands.visible = true
      return
    end
  end
  #--------------------------------------------------------------------------
  # * main场景主处理
  #--------------------------------------------------------------------------
  def main
    #背景
    @background = Sprite.new
    @background.bitmap = RPG::Cache.tetris("background")
    #主内容
    @board = Sprite.new
    @board.x = 60
    @board.y = 10
    @board.bitmap = Bitmap.new(230, 460)
    #下一个方块预告框
    @follower = Sprite.new
    @follower.x = 350
    @follower.y = 51
    @follower.bitmap = Bitmap.new(230, 124)
    redraw_follower
    #难度框
    @degree = Sprite.new
    @degree.x = 369
    @degree.y = 245
    @degree.bitmap = Bitmap.new(198, 22)
    redraw_degree
    #计分框
    @scorer = Sprite.new
    @scorer.x = 369
    @scorer.y = 337
    @scorer.bitmap = Bitmap.new(198, 22)
    redraw_scorer
    #其他的拓展内容
    #菜单
    @commands = Window_TetrisCommand.new(160, ["继续", "重开", "影子", "顺时针", "返回"])
    @shadow = true#可套到Game_Temp中使用
    @clockwise = true
    @commands.opacity = 200
    @commands.x = (640 - @commands.width) / 2
    @commands.y = (480 - @commands.height) / 2
    @commands.index = -1
    @commands.active = false
    @commands.visible = false
    Graphics.transition(8, "Graphics/Transitions/020-Flat01")
    while $scene == self
      Graphics.update
      Input.update
      update
    end
    Graphics.freeze
    @background.bitmap.dispose
    @background.dispose
    @board.bitmap.dispose
    @board.dispose
    @follower.bitmap.dispose
    @follower.dispose
    @degree.bitmap.dispose
    @degree.dispose
    @scorer.bitmap.dispose
    @scorer.dispose
    @commands.dispose
  end
  #--------------------------------------------------------------------------
  # * update场景主刷新
  #--------------------------------------------------------------------------
  def update
    if @commands.active
      update_command
      return
    end
    fall
    keyboard
    redraw_board
  end
  #--------------------------------------------------------------------------
  # * update_command刷新菜单
  #--------------------------------------------------------------------------
  def update_command
    @commands.update
    if Input.trigger?(Input::B)
      $game_system.se_play($data_system.cancel_se)
      @commands.index = -1
      @commands.active = false
      @commands.visible = false
      return
    end
    if Input.trigger?(Input::C)
      $game_system.se_play($data_system.decision_se)
      case @commands.index
      when 0
        @commands.index = -1
        @commands.active = false
        @commands.visible = false
      when 1
        for x in 0..9
          for y in 0..19
            @drawn[x, y] = 0
          end
        end
        reset_active
        @score = 0
        @count = 0
        redraw_board
        redraw_follower
        redraw_scorer
        @commands.index = -1
        @commands.active = false
        @commands.visible = false
      when 2
        if @shadow
          @commands.disable_item(2)
          @shadow = false
        else
          @commands.draw_item(2)
          @shadow = true
        end
      when 3
        if @clockwise
          @commands.replace_commands(3, "逆时针")
          @clockwise = false
          @commands.disable_item(2) unless @shadow
        else
          @commands.replace_commands(3, "顺时针")
          @clockwise = true
          @commands.disable_item(2) unless @shadow
        end
      when 4
        $scene = Scene_Map.new
      end
    end
  end
end
class Window_TetrisCommand < Window_Command
  def replace_commands(index, command)
    @commands[index] = command
    draw_item(index)
  end
  #--------------------------------------------------------------------------
  # * Refresh
  #--------------------------------------------------------------------------
  def refresh
    for i in 0...@item_max
      draw_item(i)
    end
  end
  #--------------------------------------------------------------------------
  # * Draw Item
  #     index : item number
  #     color : text color
  #--------------------------------------------------------------------------
  def draw_item(index, color = normal_color)
    self.contents.font.color = color
    rect = Rect.new(4, 32 * index, self.contents.width - 8, 32)
    self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
    self.contents.draw_text(rect, @commands[index], 1)
  end
end[/pre][/fold]

素材区好人

梦石
5
星屑
12559
在线时间
4345 小时
注册时间
2011-7-21
回帖
2264

超评测参与极短28参与极短27参与极短26参与极短25参与极短24参与极短23参与极短22参与极短21参与

发表于 2023-8-25 02:45:14 | 显示全部楼层
请问要如何使用条件分歧判断游戏的胜利和失败?

点评

在print那里加上一些开关、变量判断就行了  发表于 2023-8-27 09:50
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部