设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 1569|回复: 12
打印 上一主题 下一主题

[已经解决] 求改良打砖块的反弹方法

[复制链接]

Lv5.捕梦者 (管理员)

老黄鸡

梦石
0
星屑
39887
在线时间
7495 小时
注册时间
2009-7-6
帖子
13485

开拓者贵宾

1
发表于 2022-5-16 21:31:19 | 显示全部楼层
老问题了
第一种比较凑合的办法,当计算下一帧应该抵达的球与砖块重合时,强制把X/Y坐标改为贴到目标砖块与向量方向相反的那一面,然后改变力的方向
这种方向简单效果也凑合

第二种其实就是物理引擎里的射线检测,从球的四个角出发,计算4条射线是否与砖块四个边的线段相交,这个其实初中还是高中就学过,两条线求交点
自己研究一下吧
RGDirect - DirectX驱动的RGSS,点我了解.
RM全系列成套系统定制请联系QQ1213237796
不接受对其他插件维护的委托
回复

使用道具 举报

Lv5.捕梦者 (管理员)

老黄鸡

梦石
0
星屑
39887
在线时间
7495 小时
注册时间
2009-7-6
帖子
13485

开拓者贵宾

2
发表于 2022-5-17 18:02:20 | 显示全部楼层
做了一点小重构方便自己理解
基本功能看上去是没问题了,细节没考虑,代码也未优化
楼主参考参考就行了

RUBY 代码复制
  1. $imported ||= {}
  2. $imported["AI-BreakoutClone"] = true
  3.  
  4. module BREAKOUT_CLONE
  5.   STAGE = {}
  6.   STAGE[0] = [
  7.   [7, 15, 0], [7, 91, 0], [7, 167, 0], [7, 242, 0], [7, 317, 0], [7, 393, 0], [7, 469, 0],
  8.   [6, 15, 40], [6, 91, 40], [6, 167, 40], [6, 242, 40], [6, 317, 40], [6, 393, 40], [6, 469, 40],
  9.   [3, 15, 80], [1, 91, 80], [1, 167, 80], [1, 242, 80], [1, 317, 80], [1, 393, 80], [1, 469, 80],
  10.   [4, 15, 120], [2, 91, 120], [1, 167, 120], [1, 242, 120], [1, 317, 120], [2, 393, 120], [4, 469, 120],
  11.   [5, 15, 160], [3, 91, 160], [1, 167, 160], [1, 242, 160], [1, 317, 160], [3, 393, 160], [5, 469, 160],
  12.   [6, 15, 200], [4, 91, 200], [2, 167, 200], [1, 242, 200], [2, 317, 200], [4, 393, 200], [6, 469, 200],
  13.   [7, 15, 240], [6, 91, 240], [4, 167, 240], [1, 242, 240], [4, 317, 240], [6, 393, 240], [7, 469, 240]
  14.   ]
  15. end
  16.  
  17. module Cache
  18.   def self.breakout_clone(filename)
  19.     load_bitmap("Graphics/Breakout_Clone/", filename)
  20.   end
  21. end
  22.  
  23. module SceneManager
  24.   class << self; alias :breakout_clone_first_scene_class :first_scene_class; end
  25.   def self.first_scene_class
  26.     if $imported["AI-BreakoutClone"] && !$BTEST
  27.       Graphics.resize_screen(544, 480)
  28.       return Scene_Breakout_Clone
  29.     end
  30.     breakout_clone_first_scene_class
  31.   end
  32. end
  33.  
  34. class Game_Temp
  35.  
  36.   attr_accessor :breakout_clone_start
  37.   attr_accessor :breakout_clone_gameover
  38.  
  39.   alias breakout_clone_initialize initialize
  40.   def initialize
  41.     breakout_clone_initialize
  42.     @breakout_clone_start = false
  43.     @breakout_clone_gameover = false
  44.   end
  45.  
  46.   def set_breakout_clone_start(flag); @breakout_clone_start = flag; end
  47.   def reset_breakout_clone_start; @breakout_clone_start = false; end
  48.   def breakout_clone_start?; @breakout_clone_start == true; end
  49.  
  50.   def set_breakout_clone_gameover(flag); @breakout_clone_gameover = flag; end
  51.   def reset_breakout_clone_gameover; @breakout_clone_gameover = false; end
  52.   def breakout_clone_gameover?; @breakout_clone_gameover == true; end
  53.  
  54. end
  55.  
  56. class Breakout_Object
  57.  
  58.   include Math
  59.  
  60.   Vector2 = Struct.new(:x, :y) do
  61.     include Math
  62.     def magnitude
  63.       sqrt(x*x+y*y)
  64.     end
  65.     def normalized
  66.       return Vector2.new(0.0,0.0) if y == 0 && x == 0
  67.       a = atan2(y,x)
  68.       Vector2.new(cos(a),sin(a))
  69.     end
  70.     def *(rate)
  71.       Vector2.new(x*rate,y*rate)
  72.     end
  73.   end
  74.  
  75.   Segment = Struct.new(:x1,:y1,:x2,:y2,:vertical,:hoster,:offset)
  76.   Ray = Struct.new(:x1,:y1,:x2,:y2,:hoster,:offset) do
  77.     include Math
  78.     def vector
  79.       Vector2.new(x2-x1,y2-y1)
  80.     end
  81.   end
  82.  
  83.   attr_accessor :name
  84.   attr_accessor :x
  85.   attr_accessor :y
  86.   attr_accessor :width
  87.   attr_accessor :height
  88.   attr_accessor :force
  89.   attr_accessor :bounce
  90.   attr_accessor :mass
  91.  
  92.   def initialize
  93.     init_members_internal
  94.     init_members
  95.   end
  96.  
  97.   def init_members_internal
  98.     @name = ''
  99.     @x = 0.0
  100.     @y = 0.0
  101.     @width = 0.0
  102.     @height = 0.0
  103.     @force = Vector2.new(0.0,0.0)
  104.     @bounce = 1.0
  105.     @mass = 0
  106.     @alive = true
  107.     @static = true
  108.   end
  109.  
  110.   def next_x
  111.     @x + @force.x
  112.   end
  113.  
  114.   def next_y
  115.     @y + @force.y
  116.   end
  117.  
  118.   def x=(v)
  119.     @x = v.to_f
  120.   end
  121.  
  122.   def y=(v)
  123.     @y = v.to_f
  124.   end
  125.  
  126.   def ox
  127.     @x + @width / 2
  128.   end
  129.  
  130.   def oy
  131.     @y + @height / 2
  132.   end
  133.  
  134.   def init_members
  135.     #
  136.   end
  137.  
  138.   def update
  139.     #
  140.   end
  141.  
  142.   def take_damage(source)
  143.     #
  144.   end
  145.  
  146.   def is_alive?
  147.     @alive
  148.   end
  149.  
  150.   def is_static?
  151.     @static
  152.   end
  153.  
  154.   def bitmap
  155.   end
  156.  
  157.   def collide(target, info)
  158.     self.force.x *= -1.0 * target.bounce * self.bounce if info.seg.vertical
  159.     self.force.y *= -1.0 * target.bounce * self.bounce unless info.seg.vertical
  160.   end
  161.  
  162.   def on_collide(source, info)
  163.     #
  164.   end
  165.  
  166.   def push(target, vertical)
  167.     self.force.x = 0.0
  168.     self.force.y = 0.0
  169.     target.force.x += self.force.x * target.bounce * self.bounce if vertical
  170.     target.force.y += self.force.y * target.bounce * self.bounce unless vertical
  171.   end
  172.  
  173.   def get_segments
  174.     result = Array.new(4)
  175.     result[0] = Segment.new(@x,@y+@height,@x,@y,true,self,[0,0])
  176.     result[1] = Segment.new(@x,@y,@x+@width,@y,false,self,[-1,0])
  177.     result[2] = Segment.new(@x+@width,@y,@x+@width,@y+@height,true,self,[0,-1])
  178.     result[3] = Segment.new(@x+@width,@y+@height,@x,@y+@height,false,self,[-1,-1])
  179.     result
  180.   end
  181.  
  182.   def get_raycastor(power)
  183.     af = @force.normalized * power
  184.     result = Array.new(4)
  185.     result[0] = Ray.new(@x,@y,@x+af.x,@y+af.y,self,[0,0])
  186.     result[1] = Ray.new(@x+@width,@y,@x+@width+af.x,@y+af.y,self,[-1,0])
  187.     result[2] = Ray.new(@x,@y+@height,@x+af.x,@y+@height+af.y,self,[0,-1])
  188.     result[3] = Ray.new(@x+@width,@y+@height,@x+@width+af.x,@y+@height+af.y,self,[-1,-1])
  189.     result
  190.   end
  191.  
  192. end
  193.  
  194. class Breakout_World < Breakout_Object
  195.  
  196.   def init_members
  197.     @name = 'world'
  198.     @mass = 10
  199.   end
  200.  
  201. end
  202.  
  203. class Breakout_Paddle < Breakout_Object
  204.  
  205.   def init_members
  206.     @name = 'paddle'
  207.     @static = false
  208.     @mass = 5
  209.   end
  210.  
  211.   def collide(target, vertical)
  212.     self.force.x = 0 if vertical
  213.     self.force.y = 0 unless vertical
  214.   end
  215.  
  216.   def on_collide(source, info)
  217.     if source.name == 'world'
  218.       self.force.x = 0 if info.seg.vertical
  219.       self.force.y = 0 unless info.seg.vertical
  220.     end
  221.   end
  222.  
  223.   def update
  224.     if Input.press?(:LEFT)
  225.       @force.x = -8
  226.     elsif Input.press?(:RIGHT)
  227.       @force.x = 8
  228.     else
  229.       @force.x = 0
  230.     end
  231.     @force.y = 0
  232.   end
  233.  
  234.   def bitmap
  235.     Cache.breakout_clone("paddle")
  236.   end
  237.  
  238. end
  239.  
  240. class Breakout_Ball < Breakout_Object
  241.  
  242.   def init_members
  243.     @name = 'ball'
  244.     @static = false
  245.     @speed = 4
  246.     @force.x = ([-(rand(@speed) + 1), rand(@speed) + 1][rand(2)]).to_f
  247.     @force.y = -(@speed + 1).to_f
  248.     @mass = 1
  249.   end
  250.  
  251.   def on_collide(source, info)
  252.     if source.name == 'paddle'
  253.       dir_force = self.force.magnitude
  254.       a = atan2(self.oy - source.oy,self.ox - source.ox)
  255.       self.force.x = cos(a) * dir_force
  256.       self.force.y = sin(a) * dir_force
  257.     elsif source.name == 'world'
  258.       if self.y > Graphics.height
  259.         die
  260.       else
  261.         collide(source, info)
  262.       end
  263.     else
  264.       collide(source, info)
  265.     end
  266.   end
  267.  
  268.   def die
  269.     p 'game over'
  270.     @static = true
  271.   end
  272.  
  273.   def bitmap
  274.     Cache.breakout_clone("ball")
  275.   end
  276.  
  277. end
  278.  
  279. class Breakout_Brick < Breakout_Object
  280.  
  281.   attr_accessor :life
  282.  
  283.   def init_members
  284.     @name = 'brick'
  285.     @life = 0
  286.     @mass = 5
  287.   end
  288.  
  289.   def take_damage(source)
  290.     if @life > 1
  291.       @life-=1
  292.     else
  293.       @life = 0
  294.       @alive = false
  295.     end
  296.   end
  297.  
  298.   def on_collide(source, info)
  299.     take_damage(source) if source.name == 'ball'
  300.   end
  301.  
  302.   def bitmap
  303.     Cache.breakout_clone("brick")
  304.   end
  305.  
  306. end
  307.  
  308. class Sprite_Breakout_Single < Sprite
  309.  
  310.   def initialize(object)
  311.     super()
  312.     @target = object
  313.     refresh
  314.   end
  315.  
  316.   def refresh
  317.     self.bitmap = @target.bitmap
  318.   end
  319.  
  320.   def update
  321.     return unless is_alive?
  322.     self.x = @target.x.to_i
  323.     self.y = @target.y.to_i
  324.   end
  325.  
  326.   def is_alive?
  327.     @target.is_alive?
  328.   end
  329.  
  330. end
  331.  
  332. class Sprite_Breakout_Block < Sprite_Breakout_Single
  333.  
  334.   def initialize(object)
  335.     super
  336.     @life = @target.life
  337.   end
  338.  
  339.   def update
  340.     super
  341.     return unless is_alive?
  342.     if @life != @target.life
  343.       @life = @target.life
  344.       refresh
  345.     end
  346.   end
  347.  
  348.   def refresh
  349.     super
  350.     sy = (@target.life - 1) * 24
  351.     self.src_rect.set(0, sy, 68, 24)
  352.   end
  353.  
  354. end
  355.  
  356. class Breakout_Manager
  357.  
  358.   include Math
  359.  
  360.   IntersectionInfo = Struct.new(:x,:y,:t,:distance,:ray,:seg)
  361.  
  362.   def initialize(objects)
  363.     @objects = objects
  364.   end
  365.  
  366.   def get_objects_inrect(x01,x02,y01,y02,hoster)
  367.     @objects.find_all do |obj|
  368.       if hoster == obj
  369.         false
  370.       else
  371.         x11,x12,y11,y12 = obj.x,obj.x+obj.width,obj.y,obj.y+obj.height
  372.         zx = (x01 + x02 -x11 - x12).abs
  373.         x  = (x01 - x02).abs + (x11 - x12).abs
  374.         zy = (y01 + y02 - y11 - y12).abs
  375.         y  = (y01 - y02).abs + (y11 - y12).abs
  376.         zx <= x && zy <= y
  377.       end
  378.     end
  379.   end
  380.  
  381.   def intersection(ox,oy,x,y,ray,segment)
  382.     dx = x - ox
  383.     dy = y - oy
  384.     distance = sqrt(dx*dx+dy*dy)
  385.     return IntersectionInfo.new(x,y,0,distance,ray,segment)
  386.   end
  387.  
  388.   def get_intersection(ray,segment)
  389.     ax,ay,bx,by = ray.x1,ray.y1,ray.x2,ray.y2
  390.     cx,cy,dx,dy = segment.x1,segment.y1,segment.x2,segment.y2
  391.     area_abc = (ax - cx) * (by - cy) - (ay - cy) * (bx - cx)
  392.     area_abd = (ax - dx) * (by - dy) - (ay - dy) * (bx - dx)
  393.     return nil if area_abc*area_abd >= 0
  394.     area_cda = (cx - ax) * (dy - ay) - (cy - ay) * (dx - ax)
  395.     area_cdb = area_cda + area_abc - area_abd
  396.     return nil if area_cda*area_cdb >= 0
  397.     t = area_cda / (area_abd-area_abc)
  398.     fdx= t*(bx - ax)
  399.     fdy= t*(by - ay)
  400.     x = ax+fdx
  401.     y = ay+fdy
  402.     distance = sqrt(fdx*fdx+fdy*fdy)
  403.     return IntersectionInfo.new(x,y,t,distance,ray,segment)
  404.   end
  405.  
  406.   def get_collide(obj,targets,power)
  407.     segments = Array.new(targets.size * 4)
  408.     targets.each_with_index do |target,i|
  409.       target.get_segments.each_with_index do |seg,j|
  410.         segments[i*4+j] = seg
  411.       end
  412.     end
  413.     all_point = []
  414.     castor_list = obj.get_raycastor(power)
  415.     castor_list.each do |castor|
  416.       segments.each do |seg|
  417.         info = get_intersection(castor,seg)
  418.         all_point << info if info
  419.       end
  420.     end
  421.     return [nil,0] if all_point.size == 0
  422.     all_point.sort!{|a,b| a.distance - b.distance}
  423.     result = all_point.first
  424.     [result,power - result.distance]
  425.   end
  426.  
  427.   def get_fix_x(speed,seg)
  428.     return 0.0 if speed.x == 0 || !seg.vertical
  429.     speed.x > 0 ? -0.5 : 0.5
  430.   end
  431.  
  432.   def get_fix_y(speed,seg)
  433.     return 0.0 if speed.y == 0 || seg.vertical
  434.     speed.y > 0 ? -0.5 : 0.5
  435.   end
  436.  
  437.   def update
  438.     @objects = @objects.find_all do |obj|
  439.       obj.update
  440.       unless obj.is_static?
  441.         nx = obj.next_x
  442.         ny = obj.next_y
  443.         xlist = [obj.x,obj.x+obj.width,nx,nx+obj.width]
  444.         ylist = [obj.y,obj.y+obj.height,ny,ny+obj.height]
  445.         targets = get_objects_inrect(xlist.min,xlist.max,ylist.min,ylist.max,obj)
  446.         power = obj.force.magnitude
  447.         base_power = power
  448.         count = 0
  449.         while(power > 0)
  450.           collide_info = get_collide(obj,targets,power)
  451.           if collide_info[0]
  452.             info = collide_info[0]
  453.             seg = info.seg
  454.             ray = info.ray
  455.             speed = ray.vector
  456.             obj.x = info.x + ray.offset[0]*obj.width + get_fix_x(speed,seg)
  457.             obj.y = info.y + ray.offset[1]*obj.height + get_fix_y(speed,seg)
  458.             obj.on_collide(seg.hoster, info)
  459.             seg.hoster.on_collide(obj, info)
  460.           else
  461.             obj.x += obj.force.x * power / base_power
  462.             obj.y += obj.force.y * power / base_power
  463.           end
  464.           count+=1
  465.           power = collide_info[1]
  466.           power = 0 if count > 2
  467.         end
  468.       end
  469.       obj.is_alive?
  470.     end
  471.   end
  472.  
  473. end
  474.  
  475. class Scene_Breakout_Clone < Scene_Base
  476.  
  477.   include BREAKOUT_CLONE
  478.  
  479.   def start
  480.     super
  481.     init_members
  482.     create_breakout_clone_paddle
  483.     create_breakout_clone_ball
  484.     create_breakout_clone_brick
  485.     create_breakout_clone_worldbox
  486.     create_manager
  487.   end
  488.  
  489.   def init_members
  490.     @objects = []
  491.     @sprites = []
  492.     @cache_bitmaps = {
  493.     'paddle'=>Cache.breakout_clone("paddle"),
  494.     'ball'=>Cache.breakout_clone("ball"),
  495.     'brick'=>Cache.breakout_clone("brick")
  496.     }
  497.   end
  498.  
  499.   def create_breakout_clone_paddle
  500.     bitmap = @cache_bitmaps['paddle']
  501.     @paddle = Breakout_Paddle.new
  502.     @paddle.width = bitmap.width
  503.     @paddle.height = bitmap.height
  504.     @paddle.x = Graphics.width / 2 - @paddle.width / 2
  505.     @paddle.y = Graphics.height - @paddle.height
  506.     @objects << @paddle
  507.     @sprites << Sprite_Breakout_Single.new(@paddle)
  508.   end
  509.  
  510.   def create_breakout_clone_ball
  511.     bitmap = @cache_bitmaps['ball']
  512.     ball = Breakout_Ball.new
  513.     $tests = ball
  514.     ball.width = bitmap.width
  515.     ball.height = bitmap.height
  516.     ball.x = Graphics.width / 2 - ball.width / 2
  517.     ball.y = @paddle.y - (@paddle.height + ball.height) / 2
  518.     @objects << ball
  519.     @sprites << Sprite_Breakout_Single.new(ball)
  520.   end
  521.  
  522.   def create_breakout_clone_brick
  523.     STAGE[0].each do |brick|
  524.       brick_obj = Breakout_Brick.new
  525.       brick_obj.width = 68
  526.       brick_obj.height = 24
  527.       brick_obj.life = brick[0]
  528.       brick_obj.x = brick[1]
  529.       brick_obj.y = brick[2]
  530.       @objects << brick_obj
  531.       @sprites << Sprite_Breakout_Block.new(brick_obj)
  532.     end
  533.   end
  534.  
  535.   def create_breakout_clone_worldbox
  536.     worldbox = Breakout_World.new
  537.     worldbox.width = Graphics.width
  538.     worldbox.height = Graphics.height + 32
  539.     @objects << worldbox
  540.   end
  541.  
  542.   def create_manager
  543.     @manager = Breakout_Manager.new(@objects)
  544.   end
  545.  
  546.   def terminate
  547.     super
  548.     dispose_all
  549.   end
  550.  
  551.   def dispose_all
  552.     @sprites.each{|spr| spr.dispose}
  553.   end
  554.  
  555.   def update
  556.     super
  557.     @manager.update
  558.     @sprites = @sprites.find_all do |spr|
  559.       spr.update
  560.       ret = spr.is_alive?
  561.       spr.dispose unless ret
  562.       ret
  563.     end
  564.   end
  565.  
  566. end
RGDirect - DirectX驱动的RGSS,点我了解.
RM全系列成套系统定制请联系QQ1213237796
不接受对其他插件维护的委托
回复

使用道具 举报

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

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-5-16 07:02

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表