рдПрдХ рдореЛрдмрд╛рдЗрд▓ PvP рд╢реВрдЯрд░ рдХреЗ рд▓рд┐рдП рднреМрддрд┐рдХреА рдФрд░ рд╣рдордиреЗ ECS рдХреЗ рд╕рд╛рде рдХреИрд╕реЗ рджреЛрд╕реНрддреА рдХреА

рд╕рднреА рдХреЛ рдирдорд╕реНрдХрд╛рд░! рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ, рд╣рдо рдПрдХ рдорд▓реНрдЯреАрдкреНрд▓реЗрдпрд░ рд╢реВрдЯрд░ рдХреЗ рд▓рд┐рдП рднреМрддрд┐рдХ рдЗрдВрдЬрди рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд╡реНрдпрдХреНрддрд┐рдЧрдд рдЕрдиреБрднрд╡ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░реЗрдВрдЧреЗ рдФрд░ рдореБрдЦреНрдп рд░реВрдк рд╕реЗ рднреМрддрд┐рдХреА рдФрд░ рдИрд╕реАрдПрд╕ рдХреА рдмрд╛рддрдЪреАрдд рдкрд░ рдзреНрдпрд╛рди рдХреЗрдВрджреНрд░рд┐рдд рдХрд░реЗрдВрдЧреЗ: рд╣рдордиреЗ рдХрд╛рдо рдХреЗ рджреМрд░рд╛рди рдХрд┐рд╕ рддрд░рд╣ рдХреА рд░реЗрдХ рдХреА, рд╣рдордиреЗ рдХреНрдпрд╛ рд╕реАрдЦрд╛, рд╣рдо рд╡рд┐рд╢рд┐рд╖реНрдЯ рд╕рдорд╛рдзрд╛рдиреЛрдВ рдкрд░ рдХреНрдпреЛрдВ рдмрд╕реЗред



рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рдЖрдЗрдП рдЬрд╛рдиреЗрдВ рдХрд┐ рднреМрддрд┐рдХ рдЗрдВрдЬрди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХреНрдпреЛрдВ рд╣реИред рдХреЛрдИ рд╕рд╛рд░реНрд╡рднреМрдорд┐рдХ рдЙрддреНрддрд░ рдирд╣реАрдВ рд╣реИ: рдкреНрд░рддреНрдпреЗрдХ рдЦреЗрд▓ рдореЗрдВ рдпрд╣ рдЕрдкрдиреЗ рдЙрджреНрджреЗрд╢реНрдп рдХреЛ рдкреВрд░рд╛ рдХрд░рддрд╛ рд╣реИред рдХреБрдЫ рдЦреЗрд▓ рдЦрд┐рд▓рд╛рдбрд╝реА рдХреЛ рдбреБрдмреЛрдиреЗ рдХреЗ рдкреНрд░рднрд╛рд╡ рдХреЛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рджреБрдирд┐рдпрд╛ рдореЗрдВ рд╡рд╕реНрддреБрдУрдВ рдХреЗ рд╡реНрдпрд╡рд╣рд╛рд░ рдХреЛ рд╕рд╣реА рдврдВрдЧ рд╕реЗ рдЕрдиреБрдХрд░рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рднреМрддрд┐рдХ рдЗрдВрдЬрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред рджреВрд╕рд░реЛрдВ рдореЗрдВ, рднреМрддрд┐рдХреА рдЧреЗрдордкреНрд▓реЗ рдХрд╛ рдЖрдзрд╛рд░ рд╣реИ - рдЬреИрд╕реЗ рдХрд┐, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдПрдВрдЧреНрд░реА рдмрд░реНрдбреНрд╕ рдФрд░ рд░реЗрдб рдлреИрдХреНрд╢рдиред рдРрд╕реЗ "рд╕реИрдВрдбрдмреЙрдХреНрд╕" рднреА рд╣реИрдВ рдЬрд┐рдирдореЗрдВ рд╢рд╛рд░реАрд░рд┐рдХ рдирд┐рдпрдо рд╕рд╛рдорд╛рдиреНрдп рд▓реЛрдЧреЛрдВ рд╕реЗ рднрд┐рдиреНрди рд╣реЛрддреЗ рд╣реИрдВ рдФрд░ рдЗрд╕ рддрд░рд╣ рдЧреЗрдордкреНрд▓реЗ рдХреЛ рдЕрдзрд┐рдХ рд░реЛрдЪрдХ рдФрд░ рдЕрд╕рд╛рдорд╛рдиреНрдп рдмрдирд╛рддреЗ рд╣реИрдВ (рдкреЛрд░реНрдЯрд▓, рдП рд╕реНрд▓реЛрдЕрд░ рд╕реНрдкреАрдб рдСрдлрд╝ рд▓рд╛рдЗрдЯ)ред

рдПрдХ рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдВрдЧ рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╕реЗ, рднреМрддрд┐рдХ рдЗрдВрдЬрди рдПрдХ рдЦреЗрд▓ рдореЗрдВ рд╡рд╕реНрддреБрдУрдВ рдХреЗ рд╡реНрдпрд╡рд╣рд╛рд░ рдХрд╛ рдЕрдиреБрдХрд░рдг рдХрд░рдирд╛ рдЖрд╕рд╛рди рдмрдирд╛рддрд╛ рд╣реИред рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рдпрд╣ рдПрдХ рдкреБрд╕реНрддрдХрд╛рд▓рдп рд╣реИ рдЬреЛ рд╡рд╕реНрддреБрдУрдВ рдХреЗ рднреМрддрд┐рдХ рдЧреБрдгреЛрдВ рдХрд╛ рд╡рд░реНрдгрди рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рддрд╛ рд╣реИред рдПрдХ рднреМрддрд┐рдХ рдЗрдВрдЬрди рдХреА рдЙрдкрд╕реНрдерд┐рддрд┐ рдореЗрдВ, рд╣рдореЗрдВ рдирд┐рдХрд╛рдпреЛрдВ рдФрд░ рд╕рд╛рд░реНрд╡рднреМрдорд┐рдХ рдХрд╛рдиреВрдиреЛрдВ рдХреЗ рдмреАрдЪ рдмрд╛рддрдЪреАрдд рдХреА рдПрдХ рдкреНрд░рдгрд╛рд▓реА рд╡рд┐рдХрд╕рд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИ рдЬрд┐рд╕рдХреЗ рджреНрд╡рд╛рд░рд╛ рдЦреЗрд▓ рдХреА рджреБрдирд┐рдпрд╛ рдЬреАрд╡рд┐рдд рд░рд╣реЗрдЧреАред рдпрд╣ рдПрдХ рдЯрди рд╕рдордп рдФрд░ рд╡рд┐рдХрд╛рд╕ рдХреЗ рдкреНрд░рдпрд╛рд╕ рдХреЛ рдмрдЪрд╛рддрд╛ рд╣реИред

рдЫрд╡рд┐
рдКрдкрд░ рджрд┐рдП рдЧрдП рдЖрд░реЗрдЦ рдкреНрд▓реЗрдпрд░ рдХреЗ рд╕рд╛рд░, рдЙрд╕рдХреЗ рдШрдЯрдХреЛрдВ рдФрд░ рдЙрдирдХреЗ рдбреЗрдЯрд╛ рдФрд░ рдЙрди рд╕рд┐рд╕реНрдЯрдо рдХрд╛ рд╡рд░реНрдгрди рдХрд░рддрд╛ рд╣реИ рдЬреЛ рдЦрд┐рд▓рд╛рдбрд╝реА рдФрд░ рдЙрд╕рдХреЗ рдШрдЯрдХреЛрдВ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВред рдЖрд░реЗрдЦ рдореЗрдВ рдореБрдЦреНрдп рд╡рд╕реНрддреБ рдЦрд┐рд▓рд╛рдбрд╝реА рд╣реИ: рд╡рд╣ рдЕрдВрддрд░рд┐рдХреНрд╖ рдореЗрдВ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░ рд╕рдХрддрд╛ рд╣реИ - рдЯреНрд░рд╛рдВрд╕рдлреЙрд░реНрдо рдФрд░ рдореВрд╡рдореЗрдВрдЯ рдШрдЯрдХ, рдореВрд╡рд╕рд┐рд╕реНрдЯрдо; рдХреБрдЫ рд╕реНрд╡рд╛рд╕реНрдереНрдп рд╣реИ рдФрд░ рдорд░ рд╕рдХрддреЗ рд╣реИрдВ - рдШрдЯрдХ рд╕реНрд╡рд╛рд╕реНрдереНрдп, рдиреБрдХрд╕рд╛рди, рдиреБрдХрд╕рд╛рди рдкреНрд░рдгрд╛рд▓реА; рдорд░рдиреЗ рдХреЗ рдмрд╛рдж рд░рд┐рд╕реНрдкрд╛рдВрд╕ рдкреЙрдЗрдВрдЯ рдкрд░ рдкреНрд░рдХрдЯ рд╣реЛрддрд╛ рд╣реИ - рд╕реНрдерд┐рддрд┐ рдХреЗ рд▓рд┐рдП рдЯреНрд░рд╛рдВрд╕рдлреЙрд░реНрдо рдШрдЯрдХ, рд░реЗрд╕реНрдкреЙрдирд╕рд┐рд╕реНрдЯрдо; рдЕрдЬреЗрдп рд╣реЛ рд╕рдХрддрд╛ рд╣реИ - рдШрдЯрдХ рдЕрдЬреЗрдпред

рдирд┐рд╢рд╛рдиреЗрдмрд╛рдЬреЛрдВ рдХреЗ рд▓рд┐рдП рдЦреЗрд▓ рднреМрддрд┐рдХреА рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреА рд╡рд┐рд╢реЗрд╖рддрд╛рдПрдВ рдХреНрдпрд╛ рд╣реИрдВ?


рд╣рдорд╛рд░реЗ рдЦреЗрд▓ рдореЗрдВ рдХреЛрдИ рдЬрдЯрд┐рд▓ рднреМрддрд┐рдХ рд╕рдВрдкрд░реНрдХ рдирд╣реАрдВ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдХрдИ рдЪреАрдЬреЗрдВ рд╣реИрдВ рдЬрд┐рдирдХреЗ рд▓рд┐рдП рдПрдХ рднреМрддрд┐рдХ рдЗрдВрдЬрди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдкреНрд░рд╛рд░рдВрдн рдореЗрдВ, рд╣рдордиреЗ рдкреВрд░реНрд╡рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд╛рдиреВрдиреЛрдВ рдХреЗ рдЕрдиреБрд╕рд╛рд░ рджреБрдирд┐рдпрд╛ рдореЗрдВ рдПрдХ рдЪрд░рд┐рддреНрд░ рдХреЛ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреА рдпреЛрдЬрдирд╛ рдмрдирд╛рдИред рдЖрдорддреМрд░ рдкрд░ рдпрд╣ рд╢рд░реАрд░ рдХреЛ рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рдЖрд╡реЗрдЧ рдпрд╛ рдирд┐рд░рдВрддрд░ рдЧрддрд┐ рджреЗрдХрд░ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЬрд┐рд╕рдХреЗ рдмрд╛рдж, рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдХреЗ рд╕рд┐рдореНрдпреВрд▓реЗрдЯ / рдЕрдкрдбреЗрдЯ рдкрджреНрдзрддрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП, рдЗрд╕рдореЗрдВ рдкрдВрдЬреАрдХреГрдд рд╕рднреА рдирд┐рдХрд╛рдпреЛрдВ рдХреЛ рдПрдХ рдХрджрдо рдЖрдЧреЗ рдмрдврд╝рд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред

рдирд┐рд╢рд╛рдиреЗрдмрд╛рдЬреЛрдВ рдореЗрдВ, 3 рдбреА рднреМрддрд┐рдХреА рдХрд╛ рдЙрдкрдпреЛрдЧ рдЕрдХреНрд╕рд░ рди рдХреЗрд╡рд▓ рдЪрд░рд┐рддреНрд░ рдЖрдВрджреЛрд▓рдиреЛрдВ рдХреЛ рдЕрдиреБрдХрд░рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдмрд▓реНрдХрд┐ рдмреБрд▓реЗрдЯ рдФрд░ рд░реЙрдХреЗрдЯ, рдЬрдВрдкрд░реНрд╕ рдХреЗ рдПрдХ рджреВрд╕рд░реЗ рдФрд░ рдкрд░реНрдпрд╛рд╡рд░рдг рдХреЗ рд╕рд╛рде рдкрд╛рддреНрд░реЛрдВ рдХреА рдмрд╛рддрдЪреАрдд рдХреЗ рд╕рд╣реА рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рд▓рд┐рдП рднреА рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдпрджрд┐ рд╢реВрдЯрд░ рдпрдерд╛рд░реНрдерд╡рд╛рджреА рд╣реЛрдиреЗ рдХрд╛ рджрд╛рд╡рд╛ рдХрд░рддрд╛ рд╣реИ рдФрд░ рд╢реВрдЯрд┐рдВрдЧ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреА рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд╕рдВрд╡реЗрджрдирд╛рдУрдВ рдХреЛ рд╡реНрдпрдХреНрдд рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реИ, рддреЛ рдЙрд╕реЗ рдмрд╕ рдПрдХ рднреМрддрд┐рдХ рдЗрдВрдЬрди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рдЬрдм рдХреЛрдИ рдЦрд┐рд▓рд╛рдбрд╝реА рдХрд┐рд╕реА рдирд┐рд╢рд╛рдиреЗ рдкрд░ рд╢реЙрдЯрдЧрди рдорд╛рд░рддрд╛ рд╣реИ, рддреЛ рд╡рд╣ рдЕрдиреБрднрд╡ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдЙрдореНрдореАрдж рдХрд░рддрд╛ рд╣реИ рдФрд░ рдПрдХ рдкрд░рд┐рдгрд╛рдо рдЬреЛ рдЬрд┐рддрдирд╛ рд╕рдВрднрд╡ рд╣реЛ рдЙрддрдирд╛ рдХрд░реАрдм рд╣реЛрддрд╛ рд╣реИ рдЬреЛ рд╡рд╣ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдирд┐рд╢рд╛рдиреЗрдмрд╛рдЬреЛрдВ рдХреЗ рдПрдХ рджреАрд░реНрдШрдХрд╛рд▓рд┐рдХ рдЦреЗрд▓ рд╕реЗ рдЬрд╛рдирддрд╛ рд╣реИ - рдХреБрдЫ рдирдпрд╛ рдЬреЛ рдореВрд▓ рд░реВрдк рд╕реЗ рдирдпрд╛ рд╣реЛрдЧрд╛, рд╡рд╣ рд╕рдмрд╕реЗ рдЕрдзрд┐рдХ рдЕрдкреНрд░рд┐рдп рд░реВрдк рд╕реЗ рдЙрд╕реЗ рдЖрд╢реНрдЪрд░реНрдпрдЪрдХрд┐рдд рдХрд░реЗрдЧрд╛ред

рд▓реЗрдХрд┐рди рд╣рдорд╛рд░реЗ рдЦреЗрд▓ рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рдХрдИ рд╕реАрдорд╛рдПрдБ рд╣реИрдВред рдЪреВрдВрдХрд┐ рд╣рдорд╛рд░рд╛ рд╢реВрдЯрд░ рдореЛрдмрд╛рдЗрд▓ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдпрд╣ рдПрдХ рджреВрд╕рд░реЗ рдХреЗ рд╕рд╛рде рдФрд░ рдЖрд╕рдкрд╛рд╕ рдХреА рджреБрдирд┐рдпрд╛ рдХреЗ рд╕рд╛рде рдкрд╛рддреНрд░реЛрдВ рдХреА рдЬрдЯрд┐рд▓ рдмрд╛рддрдЪреАрдд рдХрд╛ рдорддрд▓рдм рдирд╣реАрдВ рд╣реИ, рдЗрд╕реЗ рдЕрд╕рдорд╛рди рд╕рддрд╣ рдкрд░ рдХреВрджрдиреЗ рдХреЗ рд▓рд┐рдП рд╕реБрдВрджрд░ рдмреИрд▓рд┐рд╕реНрдЯрд┐рдХ, рд╡рд┐рдирд╛рд╢рдХрд╛рд░реА рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИред рд▓реЗрдХрд┐рди рдПрдХ рд╣реА рд╕рдордп рдореЗрдВ рдФрд░ рдПрдХ рд╣реА рдХрд╛рд░рдг рд╕реЗ, рдмрд╣реБрдд рд╕рдЦреНрдд рдпрд╛рддрд╛рдпрд╛рдд рдЖрд╡рд╢реНрдпрдХрддрд╛рдПрдВ рд╣реИрдВред рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ 3 рдбреА рднреМрддрд┐рдХреА рдмреЗрдорд╛рдиреА рд╣реЛрдЧреА: рдпрд╣ рдЕрдкрдиреЗ рдХрдВрдкреНрдпреВрдЯрд┐рдВрдЧ рд╕рдВрд╕рд╛рдзрдиреЛрдВ рдХреЗ рдХреЗрд╡рд▓ рдПрдХ рдЫреЛрдЯреЗ рд╕реЗ рд╣рд┐рд╕реНрд╕реЗ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдЧрд╛ рдФрд░ рдЕрдирд╛рд╡рд╢реНрдпрдХ рдбреЗрдЯрд╛ рдЙрддреНрдкрдиреНрди рдХрд░реЗрдЧрд╛, рдЬреЛ рдпреВрдбреАрдкреА рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╕рд░реНрд╡рд░ рдХреЗ рд╕рд╛рде рдореЛрдмрд╛рдЗрд▓ рдиреЗрдЯрд╡рд░реНрдХ рдФрд░ рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЗ рдирд┐рд░рдВрддрд░ рд╕рд┐рдВрдХреНрд░рдирд╛рдЗрдЬрд╝реЗрд╢рди рдореЗрдВ рдмрд╣реБрдд рдЕрдзрд┐рдХ рд╕реНрдерд╛рди рд▓реЗрдЧрд╛ред рдпрд╣рд╛рдВ рдпрд╣ рдпрд╛рдж рд░рдЦрдиреЗ рдпреЛрдЧреНрдп рд╣реИ рдХрд┐ рд╣рдорд╛рд░реЗ рдиреЗрдЯрд╡рд░реНрдХ рдореЙрдбрд▓ рдореЗрдВ рдЕрднреА рднреА рднрд╡рд┐рд╖реНрдпрд╡рд╛рдгреА рдФрд░ рд╕реБрд▓рд╣ рдЬреИрд╕реА рдЪреАрдЬреЗрдВ рд╣реИрдВ, рдЬрд┐рд╕рдореЗрдВ рдХреНрд▓рд╛рдЗрдВрдЯ рдкрд░ рдмрд╕реНрддрд┐рдпрд╛рдВ рднреА рд╢рд╛рдорд┐рд▓ рд╣реИрдВред рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдк, рд╣рдореЗрдВ рдкрддрд╛ рдЪрд▓рддрд╛ рд╣реИ рдХрд┐ рд░реЗрдВрдбрд░ рдФрд░ рдЕрдиреНрдп рдХреНрд▓рд╛рдЗрдВрдЯ рд╕рдмрд╕рд┐рд╕реНрдЯрдо рдХреЗ рд╕рд╛рде рд╣рд╕реНрддрдХреНрд╖реЗрдк рдХрд┐рдП рдмрд┐рдирд╛, рд╣рдорд╛рд░реА рднреМрддрд┐рдХреА рдХреЛ рд╕рдлрд▓рддрд╛рдкреВрд░реНрд╡рдХ рд▓реЙрдиреНрдЪ рдХрд░рдиреЗ рдФрд░ рдореЛрдмрд╛рдЗрд▓ рдЙрдкрдХрд░рдгреЛрдВ рдкрд░ рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЬрд┐рддрдиреА рдЬрд▓реНрджреА рд╣реЛ рд╕рдХреЗ рдХрд╛рдо рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред

рддреЛ, 3 рдбреА рднреМрддрд┐рдХреА рд╣рдореЗрдВ рд╕реВрдЯ рдирд╣реАрдВ рдХрд┐рдпрд╛ред рд▓реЗрдХрд┐рди рдпрд╣рд╛рдВ рдпрд╣ рдпрд╛рдж рд░рдЦрдиреЗ рдпреЛрдЧреНрдп рд╣реИ рдХрд┐ рднрд▓реЗ рд╣реА рдЦреЗрд▓ рддреНрд░рд┐-рдЖрдпрд╛рдореА рджрд┐рдЦрддрд╛ рд╣реИ, рдпрд╣ рддрдереНрдп рдирд╣реАрдВ рд╣реИ рдХрд┐ рдЗрд╕рдореЗрдВ рднреМрддрд┐рдХреА рднреА рддреАрди-рдЖрдпрд╛рдореА рд╣реИ: рд╕рдм рдХреБрдЫ рдПрдХ рджреВрд╕рд░реЗ рдХреЗ рд╕рд╛рде рд╡рд╕реНрддреБрдУрдВ рдХреА рдмрд╛рддрдЪреАрдд рдХреА рдкреНрд░рдХреГрддрд┐ рдХреЛ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рддрд╛ рд╣реИред рдЕрдХреНрд╕рд░ рдкреНрд░рднрд╛рд╡ рдЬреЛ 2D рднреМрддрд┐рдХреА рджреНрд╡рд╛рд░рд╛ рдХрд╡рд░ рдирд╣реАрдВ рдХрд┐рдП рдЬрд╛ рд╕рдХрддреЗ рд╣реИрдВ, рдЙрдиреНрд╣реЗрдВ рдпрд╛ рддреЛ рдЕрдиреБрдХреВрд▓рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ - рдЕрд░реНрдерд╛рдд, рдПрдХ рддрд░реНрдХ рд▓рд┐рдЦрд╛ рдЬрд╛рддрд╛ рд╣реИ рдЬреЛ рддреАрди рдЖрдпрд╛рдореА рдЗрдВрдЯрд░реИрдХреНрд╢рди рдХреА рддрд░рд╣ рджрд┐рдЦрддрд╛ рд╣реИ - рдпрд╛ рдмрд╕ рджреГрд╢реНрдп рдкреНрд░рднрд╛рд╡реЛрдВ рдХреЗ рд╕рд╛рде рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдЬреЛ рдЧреЗрдордкреНрд▓реЗ рдХреЛ рдкреНрд░рднрд╛рд╡рд┐рдд рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВред рдж рд╣реАрд░реЛрдЬ рдСрдлрд╝ рдж рд╕реНрдЯреЙрд░реНрдо, рдбрд┐рдлреЗрдВрд╕ рдСрдлрд╝ рдж рдПрдирд╕рд┐рдПрдВрдЯреНрд╕, рд▓реАрдЬреЗрдВрдб рдСрдлрд╝ рд▓реАрдЬреЗрдВрдбреНрд╕ рдХреЗ рдирд╛рдпрдХреЛрдВ рдореЗрдВ, рджреЛ-рдЖрдпрд╛рдореА рднреМрддрд┐рдХреА рдЦреЗрд▓ рдХреА рд╕рднреА рдЧреЗрдордкреНрд▓реЗ рд╕реБрд╡рд┐рдзрд╛рдПрдБ рдкреНрд░рджрд╛рди рдХрд░рддреА рд╣реИ, рдЬреЛ рддрд╕реНрд╡реАрд░ рдХреА рдЧреБрдгрд╡рддреНрддрд╛ рдХреЛ рдкреНрд░рднрд╛рд╡рд┐рдд рдХрд┐рдП рдмрд┐рдирд╛ рдпрд╛ рджреБрдирд┐рдпрд╛ рдХреЗ рдЦреЗрд▓ рдбрд┐рдЬрд╛рдЗрдирд░реЛрдВ рдФрд░ рдХрд▓рд╛рдХрд╛рд░реЛрдВ рджреНрд╡рд╛рд░рд╛ рдмрдирд╛рдИ рдЧрдИ рд╡рд┐рд╢реНрд╡рд╕рдиреАрдпрддрд╛ рдХреА рднрд╛рд╡рдирд╛ рдХреЛ рдкреНрд░рднрд╛рд╡рд┐рдд рдХрд░рддреА рд╣реИред рдЗрд╕рд▓рд┐рдП, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдЗрди рдЦреЗрд▓реЛрдВ рдореЗрдВ рдЬрдВрдкрд┐рдВрдЧ рдХреИрд░реЗрдХреНрдЯрд░ рд╣реЛрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдЙрдирдХреЗ рдЬрдВрдк рдХреА рдКрдВрдЪрд╛рдИ рдореЗрдВ рдХреЛрдИ рднреМрддрд┐рдХ рдмреЛрдз рдирд╣реАрдВ рд╣реЛрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдпрд╣ рджреНрд╡рд┐-рдЖрдпрд╛рдореА рд╕рд┐рдореБрд▓реЗрд╢рди рдореЗрдВ рдЙрддрд░рддрд╛ рд╣реИ рдФрд░ _isInTheAir рдЬреИрд╕реЗ рдХрд┐рд╕реА рддрд░рд╣ рдХреЗ рдЭрдВрдбреЗ рдХреЛ рд╕реЗрдЯ рдХрд░рддрд╛ рд╣реИ рдЬрдм рдЪрд░рд┐рддреНрд░ рд╣рд╡рд╛ рдореЗрдВ рд╣реЛрддрд╛ рд╣реИ - рддреЛ рд▓реЙрдЬрд┐рдХ рдХреА рдЧрдгрдирд╛ рдХрд░рддреЗ рд╕рдордп рдЗрд╕реЗ рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦрд╛ рдЬрд╛рддрд╛ рд╣реИред

рдЗрд╕рд▓рд┐рдП 2 рдбреА рднреМрддрд┐рдХреА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХрд╛ рдирд┐рд░реНрдгрдп рд▓рд┐рдпрд╛ рдЧрдпрд╛ред рд╣рдо рдЧреЗрдо рдХреЛ рдпреВрдирд┐рдЯреА рдореЗрдВ рд▓рд┐рдЦрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рд╕рд░реНрд╡рд░ рдпреВрдирд┐рдЯреА-рдХрдо .net рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИ, рдЬрд┐рд╕реЗ рдпреВрдирд┐рдЯреА рд╕рдордЭ рдирд╣реАрдВ рдкрд╛рддреА рд╣реИред рдЪреВрдВрдХрд┐ рд╕рд┐рдореБрд▓реЗрд╢рди рдХреЛрдб рдХреЗ рд╢реЗрд░ рдХреА рд╣рд┐рд╕реНрд╕реЗрджрд╛рд░реА рдХреНрд▓рд╛рдЗрдВрдЯ рдФрд░ рд╕рд░реНрд╡рд░ рдХреЗ рдмреАрдЪ рдЕрдлрд╡рд╛рд╣ рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдордиреЗ рдХреБрдЫ рдХреНрд░реЙрд╕-рдкреНрд▓реЗрдЯрдлреЙрд░реНрдо рдХреА рддрд▓рд╛рд╢ рд╢реБрд░реВ рдХреА - рдЕрд░реНрдерд╛рддреН, рд╢реБрджреНрдз рд╕реА # рдореЗрдВ рд▓рд┐рдЦреА рдЧрдИ рдПрдХ рднреМрддрд┐рдХ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдЬрд┐рд╕рдореЗрдВ рдмрд┐рдирд╛ рдореЛрдмрд╛рдЗрд▓ рдХреЛрдб рдХреЗ рджреБрд░реНрдШрдЯрдирд╛рдЧреНрд░рд╕реНрдд рд╣реЛрдиреЗ рдХреЗ рдЦрддрд░реЗ рдХреЛ рдЦрддреНрдо рдХрд┐рдпрд╛ рдЬрд╛рдПред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдирд┐рд╢рд╛рдиреЗрдмрд╛рдЬреЛрдВ рдХреЗ рдХрд╛рдо рдХреА рдмрд╛рд░реАрдХрд┐рдпреЛрдВ рдХреЛ рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦрддреЗ рд╣реБрдП, рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ, рд╕рд░реНрд╡рд░ рдкрд░ рд▓рдЧрд╛рддрд╛рд░ рд░рд┐рд╡рд╛рдЗрдВрдбрд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдпрд╣ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐ рдЦрд┐рд▓рд╛рдбрд╝реА рдиреЗ рдХрд╣рд╛рдБ рд╢реВрдЯрд┐рдВрдЧ рдХреА рд╣реИ, рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдпрд╣ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдерд╛ рдХрд┐ рдкреБрд╕реНрддрдХрд╛рд▓рдп рдЗрддрд┐рд╣рд╛рд╕ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░ рд╕рдХреЗ - рдЕрд░реНрдерд╛рдд, рд╣рдо рд╕рд╕реНрддреЗ рдореЗрдВ рд╢рд░реАрд░ рдХреА рд╕реНрдерд┐рддрд┐ рдХреЛ рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ рдПрди рд╕рдордп рдореЗрдВ рд╡рд╛рдкрд╕ рдлреНрд░реЗрдоред ред рдФрд░, рдЬрд╝рд╛рд╣рд┐рд░ рд╣реИ, рдкрд░рд┐рдпреЛрдЬрдирд╛ рдХреЛ рдЫреЛрдбрд╝ рдирд╣реАрдВ рджрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП: рдпрд╣ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ рдХрд┐ рд▓реЗрдЦрдХ рдЗрд╕реЗ рд╕рдорд░реНрдерди рдХрд░рддрд╛ рд╣реИ рдФрд░ рдмрдЧ рдХреЛ рдЬрд▓реНрджреА рд╕реЗ рдареАрдХ рдХрд░ рд╕рдХрддрд╛ рд╣реИ, рдпрджрд┐ рдХреЛрдИ рдСрдкрд░реЗрд╢рди рдХреЗ рджреМрд░рд╛рди рдкрд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред

рдЬреИрд╕рд╛ рдХрд┐ рдпрд╣ рдирд┐рдХрд▓рд╛, рдЙрд╕ рд╕рдордп рдмрд╣реБрдд рдХрдо рдкреБрд╕реНрддрдХрд╛рд▓рдп рд╣рдорд╛рд░реА рдЖрд╡рд╢реНрдпрдХрддрд╛рдУрдВ рдХреЛ рдкреВрд░рд╛ рдХрд░ рд╕рдХрддреЗ рдереЗред рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рдХреЗрд╡рд▓ рдПрдХ рд╣реА рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдЙрдкрдпреБрдХреНрдд рдерд╛ - VolatilePhysics ред

рдкреБрд╕реНрддрдХрд╛рд▓рдп рдЗрд╕ рджреГрд╖реНрдЯрд┐ рд╕реЗ рдЙрд▓реНрд▓реЗрдЦрдиреАрдп рд╣реИ рдХрд┐ рдпрд╣ рдПрдХрддрд╛ рдФрд░ рдПрдХрддрд╛-рджреЛрдиреЛрдВ рд╕рдорд╛рдзрд╛рдиреЛрдВ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ, рдФрд░ рдЖрдкрдХреЛ рдмреЙрдХреНрд╕ рд╕реЗ рдмрд╛рд╣рд░ рдХреА рд╡рд╕реНрддреБрдУрдВ рдХреА рдкрд┐рдЫрд▓реА рд╕реНрдерд┐рддрд┐ рдореЗрдВ рд░реИрдХрд╛рд╕реНрдЯрд┐рдХреНрд╕ рдХрд░рдиреЗ рдХреА рднреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ, рдЕрд░реНрдерд╛рддреНред рд╢реВрдЯрд░ рддрд░реНрдХ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреБрдХреНрдд рд╣реИред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдХреА рд╕реБрд╡рд┐рдзрд╛ рдЗрд╕ рддрдереНрдп рдореЗрдВ рдирд┐рд╣рд┐рдд рд╣реИ рдХрд┐ рд╕рд┐рдореНрдпреВрд▓реЗрдЯ () рдХреЗ рд╕рд┐рдореБрд▓реЗрд╢рди рдХреА рд╢реБрд░реБрдЖрдд рдХреЛ рдирд┐рдпрдВрддреНрд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рддрдВрддреНрд░ рдЖрдкрдХреЛ рдХрд┐рд╕реА рднреА рд╕рдордп рдЗрд╕рдХрд╛ рдЙрддреНрдкрд╛рджрди рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ рдЬрдм рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЛ рдЗрд╕рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рдФрд░ рдПрдХ рдЕрдиреНрдп рд╡рд┐рд╢реЗрд╖рддрд╛ - рднреМрддрд┐рдХ рд╢рд░реАрд░ рдХреЗ рд▓рд┐рдП рдЕрддрд┐рд░рд┐рдХреНрдд рдбреЗрдЯрд╛ рд▓рд┐рдЦрдиреЗ рдХреА рдХреНрд╖рдорддрд╛ред рдпрд╣ рдЬрдм рд░реЗрдХреЙрд╕реНрдЯ рдХреЗ рдкрд░рд┐рдгрд╛рдореЛрдВ рдореЗрдВ рдПрдХ рд╕рд┐рдореБрд▓реЗрд╢рди рд╕реЗ рдХрд┐рд╕реА рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЛ рд╕рдВрдмреЛрдзрд┐рдд рдХрд░рддреЗ рд╕рдордп рдЙрдкрдпреЛрдЧреА рд╣реЛ рд╕рдХрддрд╛ рд╣реИ - рд╣рд╛рд▓рд╛рдВрдХрд┐, рдпрд╣ рдкреНрд░рджрд░реНрд╢рди рдХреЛ рдмрд╣реБрдд рдХрдо рдХрд░ рджреЗрддрд╛ рд╣реИред

рдХреБрдЫ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдФрд░ рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж рдХрд┐ рдХреНрд▓рд╛рдЗрдВрдЯ рдФрд░ рд╕рд░реНрд╡рд░ рдХреНрд░реИрд╢ рдХреЗ рдмрд┐рдирд╛ VolatilePhysics рдХреЗ рд╕рд╛рде рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ рдмрд╛рддрдЪреАрдд рдХрд░рддреЗ рд╣реИрдВ, рд╣рдордиреЗ рдЗрд╕рдХрд╛ рд╡рд┐рдХрд▓реНрдк рдЪреБрдирд╛ред

рд╣рдордиреЗ ECS рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд╕рд╛рдорд╛рдиреНрдп рддрд░реАрдХреЗ рд╕реЗ рдкреБрд╕реНрддрдХрд╛рд▓рдп рдореЗрдВ рдкреНрд░рд╡реЗрд╢ рдХрд┐рдпрд╛ рдФрд░ рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдХреНрдпрд╛ рдЖрдпрд╛


VolatileWhysics рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рддреЗ рд╕рдордп рдкрд╣рд▓рд╛ рдХрджрдо VoltWorld рдХреА рднреМрддрд┐рдХ рджреБрдирд┐рдпрд╛ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд░рдирд╛ рд╣реИред рдпрд╣ рдПрдХ рдкреНрд░реЙрдХреНрд╕реА рдХреНрд▓рд╛рд╕ рд╣реИ, рдЬрд┐рд╕рдХреЗ рд╕рд╛рде рдореБрдЦреНрдп рдХрд╛рдо рд╣реЛрддрд╛ рд╣реИ: рдЯреНрдпреВрдирд┐рдВрдЧ, рд╡рд╕реНрддреБрдУрдВ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдбреЗрдЯрд╛ рдХрд╛ рдЕрдиреБрдХрд░рдг рдХрд░рдирд╛, рд░рд┐рдХреНрд╕реНрдЯ, рдЖрджрд┐ред рд╣рдордиреЗ рдЗрд╕реЗ рдПрдХ рд╡рд┐рд╢реЗрд╖ рдкрд╣рд▓реВ рдореЗрдВ рд▓рдкреЗрдЯ рджрд┐рдпрд╛ рддрд╛рдХрд┐ рднрд╡рд┐рд╖реНрдп рдореЗрдВ рд╣рдо рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рдХреБрдЫ рдФрд░ рдмрджрд▓ рд╕рдХреЗрдВред рдореБрдЦреМрдЯрд╛ рдХреЛрдб рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддрд╛ рд╣реИ:

рдХреЛрдб рджреЗрдЦреЗрдВ
public sealed class PhysicsWorld { public const int HistoryLength = 32; private readonly VoltWorld _voltWorld; private readonly Dictionary<uint, VoltBody> _cache = new Dictionary<uint, VoltBody>(); public PhysicsWorld(float deltaTime) { _voltWorld = new VoltWorld(HistoryLength) { DeltaTime = deltaTime }; } public bool HasBody(uint tag) { return _cache.ContainsKey(tag); } public VoltBody GetBody(uint tag) { VoltBody body; _cache.TryGetValue(tag, out body); return body; } public VoltRayResult RayCast(Vector2 origin, Vector2 direction, float distance, VoltBodyFilter filter, int ticksBehind) { var ray = new VoltRayCast(origin, direction.normalized, distance); var result = new VoltRayResult(); _voltWorld.RayCast(ref ray, ref result, filter, ticksBehind); return result; } public VoltRayResult CircleCast(Vector2 origin, Vector2 direction, float distance, float radius, VoltBodyFilter filter, int ticksBehind) { var ray = new VoltRayCast(origin, direction.normalized, distance); var result = new VoltRayResult(); _voltWorld.CircleCast(ref ray, radius, ref result, filter, ticksBehind); return result; } public void Update() { _voltWorld.Update(); } public void Update(uint tag) { var body = _cache[tag]; _voltWorld.Update(body, true); } public void UpdateBody(uint tag, Vector2 position, float angle) { var body = _cache[tag]; body.Set(position, angle); } public void CreateStaticCircle(Vector2 origin, float radius, uint tag) { var shape = _voltWorld.CreateCircleWorldSpace(origin, radius, 1f, 0f, 0f); var body = _voltWorld.CreateStaticBody(origin, 0, shape); body.UserData = tag; } public void CreateDynamicCircle(Vector2 origin, float radius, uint tag) { var shape = _voltWorld.CreateCircleWorldSpace(origin, radius, 1f, 0f, 0f); var body = _voltWorld.CreateDynamicBody(origin, 0, shape); body.UserData = tag; body.CollisionFilter = StaticCollisionFilter; _cache.Add(tag, body); } public void CreateStaticSquare(Vector2 origin, float rotationAngle, Vector2 extents, uint tag) { var shape = _voltWorld.CreatePolygonBodySpace(extents.GetRectFromExtents(), 1, 0, 0); var body = _voltWorld.CreateStaticBody(origin, rotationAngle, shape); body.UserData = tag; } public void CreateDynamicSquare(Vector2 origin, float rotationAngle, Vector2 extents, uint tag) { var shape = _voltWorld.CreatePolygonBodySpace(extents.GetRectFromExtents(), 1, 0, 0); var body = _voltWorld.CreateDynamicBody(origin, rotationAngle, shape); body.UserData = tag; body.CollisionFilter = StaticCollisionFilter; _cache.Add(tag, body); } public IEnumerable<VoltBody> GetBodies() { return _voltWorld.Bodies; } private static bool StaticCollisionFilter(VoltBody a, VoltBody b) { return b.IsStatic; } } 


рджреБрдирд┐рдпрд╛ рдмрдирд╛рддреЗ рд╕рдордп, рдЗрддрд┐рд╣рд╛рд╕ рдХреА рднрдпрд╛рд╡рд╣рддрд╛ рдХрд╛ рд╕рдВрдХреЗрдд рджрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ - рджреБрдирд┐рдпрд╛ рдХреА рд╕рдВрдЦреНрдпрд╛ рдмрддрд╛рддреА рд╣реИ рдХрд┐ рдкреБрд╕реНрддрдХрд╛рд▓рдп рд╕реНрдЯреЛрд░ рдХрд░реЗрдЧрд╛ред рд╣рдорд╛рд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рдЙрдирдХреА рд╕рдВрдЦреНрдпрд╛ 32: 30 рдлреНрд░реЗрдо рдкреНрд░рддрд┐ рд╕реЗрдХрдВрдб рдереА, рд╣рдореЗрдВ рддрд░реНрдХ рдХреЛ рдЕрдкрдбреЗрдЯ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдЗрд╕рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреА рдФрд░ 2 рдЕрддрд┐рд░рд┐рдХреНрдд рдорд╛рдорд▓реЗ рдореЗрдВ рд╣рдо рдбрд┐рдмрдЧрд┐рдВрдЧ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреА рд╕реАрдорд╛ рд╕реЗ рдкрд░реЗ рдЬрд╛рддреЗ рд╣реИрдВред рдпрд╣ рдХреЛрдб рдмрд╛рд╣реНрдп-рдХрд╛рд╕реНрдЯрд┐рдВрдЧ рд╡рд┐рдзрд┐рдпреЛрдВ рдХреЛ рднреА рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦрддрд╛ рд╣реИ рдЬреЛ рднреМрддрд┐рдХ рдирд┐рдХрд╛рдп рдФрд░ рд╡рд┐рднрд┐рдиреНрди рдкреНрд░рдХрд╛рд░ рдХреЗ рд░реЗрдХреЙрд╕реНрдЯ рдЙрддреНрдкрдиреНрди рдХрд░рддреЗ рд╣реИрдВред

рдЬреИрд╕рд╛ рдХрд┐ рд╣рдо рдкрд┐рдЫрд▓реЗ рд▓реЗрдЦреЛрдВ рд╕реЗ рдпрд╛рдж рдХрд░рддреЗ рд╣реИрдВ , рдИрд╕реАрдПрд╕ рджреБрдирд┐рдпрд╛ рдЕрдирд┐рд╡рд╛рд░реНрдп рд░реВрдк рд╕реЗ рдЗрд╕рдореЗрдВ рд╢рд╛рдорд┐рд▓ рд╕рднреА рдкреНрд░рдгрд╛рд▓рд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рдирд┐рд╖реНрдкрд╛рджрд┐рдд рд╡рд┐рдзрд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рдПрдХ рдирд┐рдпрдорд┐рдд рдХреЙрд▓ рдХреЗ рдЖрд╕рдкрд╛рд╕ рдШреВрдорддреА рд╣реИред рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рдгрд╛рд▓реА рдХреЗ рд╕рд╣реА рд╕реНрдерд╛рдиреЛрдВ рдореЗрдВ, рд╣рдо рдЕрдкрдиреЗ рдореБрдЦреМрдЯрд╛ рдХреЛ рдХреЙрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред рдкреНрд░рд╛рд░рдВрдн рдореЗрдВ, рд╣рдордиреЗ рднреМрддрд┐рдХ рдЗрдВрдЬрди рдХреЛ рдЪреБрдиреМрддреА рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП рдХреЛрдИ рдмреИрдЪрд┐рдВрдЧ рдирд╣реАрдВ рд▓рд┐рдЦреА, рд╣рд╛рд▓рд╛рдВрдХрд┐ рдЗрд╕ рддрд░рд╣ рдХреЗ рд╡рд┐рдЪрд╛рд░ рдереЗред рдореБрдЦреМрдЯреЗ рдХреЗ рдЕрдВрджрд░, рднреМрддрд┐рдХ рджреБрдирд┐рдпрд╛ рдХреЗ рдЕрдкрдбреЗрдЯ () рдХреЗ рд▓рд┐рдП рдПрдХ рдХреЙрд▓ рд▓рдЧрддрд╛ рд╣реИ, рдФрд░ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдЙрди рд╕рднреА рдЗрдВрдЯрд░реИрдХреНрд╢рди рдХрд╛ рдЕрдиреБрдХрд░рдг рдХрд░рддреА рд╣реИ рдЬреЛ рдкреНрд░рддрд┐ рдлреНрд░реЗрдо рдореЗрдВ рд╣реБрдИ рдереАрдВред

рдЗрд╕ рдкреНрд░рдХрд╛рд░, рднреМрддрд┐рдХреА рдХреЗ рд╕рд╛рде рдХрд╛рдо рджреЛ рдШрдЯрдХреЛрдВ рдХреЗ рд▓рд┐рдП рдЙрдмрд╛рд▓рддрд╛ рд╣реИ: рдПрдХ рдлреНрд░реЗрдо рдореЗрдВ рдЕрдВрддрд░рд┐рдХреНрд╖ рдореЗрдВ рдкрд┐рдВрдбреЛрдВ рдХреА рдПрдХ рд╕рдорд╛рди рдЧрддрд┐ рдФрд░ рд╢реВрдЯрд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рдХрдИ рд░реЗрдХреЙрд╕реНрдЯ, рдкреНрд░рднрд╛рд╡реЛрдВ рдХрд╛ рдЙрдЪрд┐рдд рд╕рдВрдЪрд╛рд▓рди рдФрд░ рдХрдИ рдЕрдиреНрдп рдЪреАрдЬреЗрдВред рд░реЗрдпрд╛рдХреЙрд╕реНрдЯ рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ рднреМрддрд┐рдХ рдирд┐рдХрд╛рдпреЛрдВ рдХреА рд╕реНрдерд┐рддрд┐ рдХреЗ рдЗрддрд┐рд╣рд╛рд╕ рдореЗрдВ рдкреНрд░рд╛рд╕рдВрдЧрд┐рдХ рд╣реИрдВред

рд╣рдорд╛рд░реЗ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЗ рдкрд░рд┐рдгрд╛рдореЛрдВ рдХреЗ рдЕрдиреБрд╕рд╛рд░, рд╣рдордиреЗ рдЬрд▓реНрджреА рд╕реЗ рдорд╣рд╕реВрд╕ рдХрд┐рдпрд╛ рдХрд┐ рдкреБрд╕реНрддрдХрд╛рд▓рдп рдЕрд▓рдЧ-рдЕрд▓рдЧ рдЧрддрд┐ рд╕реЗ рдмрд╣реБрдд рдЦрд░рд╛рдм рддрд░реАрдХреЗ рд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ, рдФрд░ рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рдЧрддрд┐ рд╕реЗ, рд╢рд░реАрд░ рдЖрд╕рд╛рдиреА рд╕реЗ рджреАрд╡рд╛рд░реЛрдВ рд╕реЗ рдЧреБрдЬрд░рдирд╛ рд╢реБрд░реВ рдХрд░ рджреЗрддрд╛ рд╣реИред рд╣рдорд╛рд░реЗ рдЗрдВрдЬрди рдореЗрдВ рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдирд┐рд░рдВрддрд░ рдЯрдХрд░рд╛рд╡ рдХрд╛ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рд╕реЗ рдЬреБрдбрд╝реА рдХреЛрдИ рднреА рд╕реЗрдЯрд┐рдВрдЧ рдирд╣реАрдВ рд╣реИред рд▓реЗрдХрд┐рди рдЙрд╕ рд╕рдордп рдмрд╛рдЬрд╛рд░ рдкрд░ рд╣рдорд╛рд░реЗ рд╕рдорд╛рдзрд╛рди рдХреЗ рд▓рд┐рдП рдХреЛрдИ рд╡рд┐рдХрд▓реНрдк рдирд╣реАрдВ рдереЗ, рдЗрд╕рд▓рд┐рдП рдореБрдЭреЗ рджреБрдирд┐рдпрд╛ рднрд░ рдореЗрдВ рдЪрд▓рддреА рд╡рд╕реНрддреБрдУрдВ рдХреЗ рдЕрдкрдиреЗ рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рд╕рд╛рде рдЖрдирд╛ рдкрдбрд╝рд╛ рдФрд░ рдИрд╕реАрдПрд╕ рдХреЗ рд╕рд╛рде рднреМрддрд┐рдХреА рдбреЗрдЯрд╛ рдХреЛ рд╕рд┐рдВрдХреНрд░рдирд╛рдЗрдЬрд╝ рдХрд░рдирд╛ рдкрдбрд╝рд╛ред рдЗрд╕рд▓рд┐рдП, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдЖрдВрджреЛрд▓рди рдкреНрд░рдгрд╛рд▓реА рдХреЗ рд▓рд┐рдП рд╣рдорд╛рд░рд╛ рдХреЛрдб рдЗрд╕ рдкреНрд░рдХрд╛рд░ рд╣реИ:

рдХреЛрдб рджреЗрдЦреЗрдВ
 using System; ... using Volatile; public sealed class MovePhysicsSystem : ExecutableSystem { private readonly PhysicsWorld _physicsWorld; private readonly CollisionFilter _moveFilter; private readonly VoltBodyFilter _collisionFilterDelegate; public MovePhysicsSystem(PhysicsWorld physicsWorld) { _physicsWorld = physicsWorld; _moveFilter = new CollisionFilter(true, CollisionLayer.ExplosiveBarrel); _collisionFilterDelegate = _moveFilter.Filter; } public override void Execute(GameState gs) { _moveFilter.State = gs; foreach (var pair in gs.WorldState.Movement) { ExecuteMovement(gs, pair.Key, pair.Value); } _physicsWorld.Update(); foreach (var pair in gs.WorldState.PhysicsDynamicBody) { if(pair.Value.IsAlive) { ExecutePhysicsDynamicBody(gs, pair.Key); } } } public override void Execute(GameState gs, uint avatarId) { _moveFilter.State = gs; var movement = gs.WorldState.Movement[avatarId]; if (movement != null) { ExecuteMovement(gs, avatarId, movement); _physicsWorld.Update(avatarId); var physicsDynamicBody = gs.WorldState.PhysicsDynamicBody[avatarId]; if (physicsDynamicBody != null && physicsDynamicBody.IsAlive) ExecutePhysicsDynamicBody(gs, avatarId); } } private void ExecutePhysicsDynamicBody(GameState gs, uint entityId) { var body = _physicsWorld.GetBody(entityId); if (body != null) { var transform = gs.WorldState.Transform[entityId]; transform.Position = body.Position; } } private void ExecuteMovement(GameState gs, uint entityId, Movement movement) { var body = _physicsWorld.GetBody(entityId); if (body != null) { float raycastRadius; if (CalculateRadius(gs, entityId, out raycastRadius)) { return; } body.AngularVelocity = 0; body.LinearVelocity = movement.Velocity; var movPhysicInfo = gs.WorldState.MovementPhysicInfo[entityId]; var collisionDirection = CircleRayCastSpeedCorrection(body, GameState.TickDurationSec, raycastRadius); CheckMoveInWall(movement, movPhysicInfo, collisionDirection, gs.WorldState.Transform[entityId]); } } private static bool CalculateRadius(GameState gs, uint id, out float raycastRadius) { raycastRadius = 0; var circleShape = gs.WorldState.DynamicCircleCollider[id]; if (circleShape != null) { raycastRadius = circleShape.Radius; } else { var boxShape = gs.WorldState.DynamicBoxCollider[id]; if (boxShape != null) { raycastRadius = boxShape.RaycastRadius; } else { gs.Log.Error(string.Format("Physics body {0} doesn't contains shape!", id)); return true; } } return false; } private static void CheckMoveInWall(Movement movement, MovementPhysicInfo movPhysicInfo, Vector2 collisionDirection, Transform transform) { // 60 is the max angle when player move in wall and can shoot through the wall from weapon without target. const float maxAngleToWall = 60; if (movement.Velocity.IsEqual(Vector2.zero)) { if (movPhysicInfo.LastCollisionDirection.IsEqual(Vector2.zero)) { var angleToCollision = transform.Angle.GetDirection().CalculateAbsoluteAngleInDegrees(movPhysicInfo.LastCollisionDirection); movPhysicInfo.TurnOnWall = angleToCollision <= maxAngleToWall; } return; } movPhysicInfo.LastCollisionDirection = collisionDirection * -1f; if (movPhysicInfo.LastCollisionDirection.IsEqual(Vector2.zero)) { movPhysicInfo.TurnOnWall = false; movPhysicInfo.LastCollisionDirection = collisionDirection; } else { var angleToCollision = transform.Angle.GetDirection().CalculateAbsoluteAngleInDegrees(movPhysicInfo.LastCollisionDirection); movPhysicInfo.TurnOnWall = angleToCollision <= maxAngleToWall; } } // I can't believe we are using a physics engine and have to write such kludges private Vector2 CircleRayCastSpeedCorrection(VoltBody targetBody, float deltaSeconds, float rayCastRadius) { if (rayCastRadius <= 0) { return Vector2.zero; } var speed = targetBody.LinearVelocity; var position = targetBody.Position; var direction = speed * deltaSeconds; var rayCastResult = _physicsWorld.CircleCast(position + direction.normalized * 0.1f, direction, direction.magnitude, rayCastRadius, _collisionFilterDelegate, 0); if (rayCastResult.Body == null) { return Vector2.zero; } var magSpeed = speed.magnitude; if (rayCastResult.Distance > 0) { var penetratingDistance = magSpeed * deltaSeconds - rayCastResult.Distance; var sinVelocityEdge = Vector2.Dot(-speed.normalized, rayCastResult.Normal); var biasSpeed = penetratingDistance * sinVelocityEdge / deltaSeconds; var biasVector = rayCastResult.Normal * biasSpeed * 1.1f; var resultVelocity = speed + biasVector; if (magSpeed <= 0) { resultVelocity = Vector2.zero; } targetBody.LinearVelocity = resultVelocity; return rayCastResult.Normal; } var destination = rayCastResult.Body.Position; direction = destination - position; var rayCastResultToBody = _physicsWorld.RayCast(position, direction, direction.magnitude, _collisionFilterDelegate, 0); if (rayCastResultToBody.IsValid) targetBody.LinearVelocity = rayCastResultToBody.Normal * magSpeed * deltaSeconds; return rayCastResultToBody.Normal; } } 


рд╡рд┐рдЪрд╛рд░ рдпрд╣ рд╣реИ рдХрд┐ рдкреНрд░рддреНрдпреЗрдХ рдЪрд░рд┐рддреНрд░ рдХреЛ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ, рд╣рдо рдпрд╣ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐ рдЙрд╕рдХреЗ рд╕рд╛рдордиреЗ рдХреЛрдИ рдмрд╛рдзрд╛ рд╣реИ, рдЙрд╕рдХреЗ рдЖрдВрджреЛрд▓рди рдХреА рджрд┐рд╢рд╛ рдореЗрдВ рдПрдХ рд╕рд░реНрдХрд▓рдХреИрд╕реНрдЯ рдмрдирд╛рддреЗ рд╣реИрдВред CircleCast рдХреА рдЬрд░реВрд░рдд рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдЦреЗрд▓ рдореЗрдВ рдкрд╛рддреНрд░реЛрдВ рдХреЗ рдЕрдиреБрдорд╛рди рдПрдХ рд╡реГрддреНрдд рдХрд╛ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд░рддреЗ рд╣реИрдВ, рдФрд░ рд╣рдо рдирд╣реАрдВ рдЪрд╛рд╣рддреЗ рдХрд┐ рд╡реЗ рд╡рд┐рднрд┐рдиреНрди рдЬреНрдпрд╛рдорд┐рддреАрдпреЛрдВ рдХреЗ рдмреАрдЪ рдХреЛрдиреЛрдВ рдореЗрдВ рдлрдВрд╕ рдЬрд╛рдПрдВред рдлрд┐рд░ рд╣рдо рдЧрддрд┐ рдореЗрдВ рд╡реГрджреНрдзрд┐ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рднреМрддрд┐рдХ рджреБрдирд┐рдпрд╛ рдХреА рд╡рд╕реНрддреБ рдХреЛ рдПрдХ рдлреНрд░реЗрдо рдореЗрдВ рдЗрд╕рдХреА рдЧрддрд┐ рдХреЗ рд░реВрдк рдореЗрдВ рдорд╛рди рдкреНрд░рджрд╛рди рдХрд░рддреЗ рд╣реИрдВред рдЕрдЧрд▓рд╛ рдЪрд░рдг рднреМрддрд┐рдХ рдЗрдВрдЬрди рдЕрдкрдбреЗрдЯ () рдХреА рд╕рд┐рдореБрд▓реЗрд╢рди рд╡рд┐рдзрд┐ рдХреЛ рдХреЙрд▓ рдХрд░рдирд╛ рд╣реИ, рдЬреЛ рд╣рдореЗрдВ рдЙрди рд╕рднреА рд╡рд╕реНрддреБрдУрдВ рдХреЛ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рддрд╛ рд╣реИ, рдЬреЛ рдЗрддрд┐рд╣рд╛рд╕ рдореЗрдВ рдкреБрд░рд╛рдиреА рд╕реНрдерд┐рддрд┐ рдХреЛ рдПрдХ рд╕рд╛рде рджрд░реНрдЬ рдХрд░рддреЗ рд╣реИрдВред рдЗрдВрдЬрди рдХреЗ рдЕрдВрджрд░ рд╕рд┐рдореБрд▓реЗрд╢рди рдкреВрд░рд╛ рд╣реЛрдиреЗ рдХреЗ рдмрд╛рдж, рд╣рдо рдЗрд╕ рд╕рд┐рдореНрдпреБрд▓реЗрдЯреЗрдб рдбреЗрдЯрд╛ рдХреЛ рдкрдврд╝рддреЗ рд╣реИрдВ, рдЗрд╕реЗ рд╣рдорд╛рд░реЗ ECS рдХреЗ рдЯреНрд░рд╛рдВрд╕рдлреЙрд░реНрдо рдШрдЯрдХ рдореЗрдВ рдХреЙрдкреА рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдлрд┐рд░ рдЗрд╕рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдирд╛ рдЬрд╛рд░реА рд░рдЦрддреЗ рд╣реИрдВ, рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ, рдЗрд╕реЗ рдиреЗрдЯрд╡рд░реНрдХ рдкрд░ рднреЗрдЬрддреЗ рд╣реИрдВред

рднреМрддрд┐рдХреА рдореЗрдВ рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЗ рдЧрддрд┐ рдкрд░ рдЫреЛрдЯреА рдирд┐рдпрдВрддреНрд░рд┐рдд рдЪреЛрдЯреЛрдВ рдХреЗ рд╕рд╛рде рднреМрддрд┐рдХреА рдХреЛ рдЕрджреНрдпрддрди рдХрд░рдиреЗ рдореЗрдВ рдпрд╣ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдХреНрд▓рд╛рдЗрдВрдЯ рдФрд░ рд╕рд░реНрд╡рд░ рдкрд░ рднреМрддрд┐рдХреА рдореЗрдВ рд╡рд┐рд╕рдВрдЧрддрд┐рдпреЛрдВ рд╕реЗ рдирд┐рдкрдЯрдиреЗ рдореЗрдВ рдмрд╣реБрдд рдкреНрд░рднрд╛рд╡реА рд╕рд╛рдмрд┐рдд рд╣реБрдЖред рдФрд░ рдЪреВрдВрдХрд┐ рд╣рдорд╛рд░реА рднреМрддрд┐рдХреА рдирд┐рдпрддрд╛рддреНрдордХ рдирд╣реАрдВ рд╣реИ - рдЕрд░реНрдерд╛рдд, рд╕рдорд╛рди рдЗрдирдкреБрдЯ рдбреЗрдЯрд╛ рдХреЗ рд╕рд╛рде, рдЕрдиреБрдХрд╛рд░ рдкрд░рд┐рдгрд╛рдо рднрд┐рдиреНрди рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВ - рдЗрд╕ рдмрд╛рд░реЗ рдореЗрдВ рдХрдИ рдЪрд░реНрдЪрд╛рдПрдВ рд╣реБрдИ рд╣реИрдВ рдХрд┐ рдХреНрдпрд╛ рдпрд╣ рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд╛рдпрдХ рд╣реИ, рдФрд░ рдХреНрдпрд╛ рдЙрджреНрдпреЛрдЧ рдореЗрдВ рдХреЛрдИ рднреА рдРрд╕рд╛ рд╣реА рдХрд░рддрд╛ рд╣реИ, рдПрдХ рдирд┐рдпрдд рднреМрддрд┐рдХ рдЗрдВрдЬрди рд╣рд╛рде рдореЗрдВ рд╣реИред рд╕реМрднрд╛рдЧреНрдп рд╕реЗ, рд╣рдордиреЗ рдЕрдкрдиреЗ рдЧреЗрдо рдХреЗ рдиреЗрдЯрд╡рд░реНрдХ рдШрдЯрдХ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЧреЗрдо рдбреЗрд╡рд▓рдкрд░реНрд╕ рдХреЙрдиреНрдлреНрд░реЗрдВрд╕ рдореЗрдВ рдиреАрджрд░рд▓реИрдВрдб рдХреЗ рд╕реНрдЯреВрдбрд┐рдпреЛ рдХреЗ рдбреЗрд╡рд▓рдкрд░реНрд╕ рд╕реЗ рдПрдХ рдЙрддреНрдХреГрд╖реНрдЯ рд░рд┐рдкреЛрд░реНрдЯ рдкреНрд░рд╛рдкреНрдд рдХреА рдФрд░ рдорд╣рд╕реВрд╕ рдХрд┐рдпрд╛ рдХрд┐ рдРрд╕рд╛ рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рд╣реЛрддрд╛ рд╣реИред рд╕рд┐рд╕реНрдЯрдо рдХреЛ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдЗрдХрдЯреНрдард╛ рдХрд░рдиреЗ рдФрд░ рдЗрд╕реЗ рдХрдИ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдкрд░ рдЪрд▓рд╛рдиреЗ рдХреЗ рдмрд╛рдж, рд╣рдореЗрдВ 9000 рдЯрд┐рдХреЛрдВ рдХреЗ рд▓рд┐рдП рд▓рдЧрднрдЧ 50 рдЧрд▓рдд рдкреВрд░реНрд╡рд╛рдиреБрдорд╛рди рдорд┐рд▓реЗ, рдпрд╛рдиреА рдкрд╛рдВрдЪ рдорд┐рдирдЯ рдХреА рд▓рдбрд╝рд╛рдИ рдХреЗ рджреМрд░рд╛рдиред рдЗрд╕ рддрд░рд╣ рдХреА рдХрдИ рднрд╡рд┐рд╖реНрдпрд╡рд╛рдгрд┐рдпреЛрдВ рдХреЛ рдЖрд╕рд╛рдиреА рд╕реЗ рд╕реБрд▓рд╣ рддрдВрддреНрд░ рдФрд░ рдЦрд┐рд▓рд╛рдбрд╝реА рдХреА рд╕реНрдерд┐рддрд┐ рдХреЗ рджреГрд╢реНрдп рдкреНрд░рдХреНрд╖реЗрдк рджреНрд╡рд╛рд░рд╛ рд╕рдорддрд▓ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЕрдкрдиреЗ рд╕реНрд╡рдпрдВ рдХреЗ рдбреЗрдЯрд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рднреМрддрд┐рдХреА рдХреЗ рд▓рдЧрд╛рддрд╛рд░ рдореИрдиреБрдЕрд▓ рдЕрдкрдбреЗрдЯ рдХреЗ рджреМрд░рд╛рди рд╣реЛрдиреЗ рд╡рд╛рд▓реА рддреНрд░реБрдЯрд┐рдпрд╛рдВ рдирдЧрдгреНрдп рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП, рджреГрд╢реНрдп рдкреНрд░рдХреНрд╖реЗрдк рдЬрд▓реНрджреА рд╕реЗ рдЬрдЧрд╣ рд▓реЗ рд╕рдХрддрд╛ рд╣реИ - рдпрд╣ рдХреЗрд╡рд▓ рдЗрд╕рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рд╣реИ рддрд╛рдХрд┐ рдЪрд░рд┐рддреНрд░ рдореЙрдбрд▓ рдореЗрдВ рдПрдХ рджреГрд╢реНрдп рдХреВрдж рди рд╣реЛред

рдпрд╣ рдЬрд╛рдБрдЪрдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐ рдЧреНрд░рд╛рд╣рдХ рдФрд░ рд╕рд░реНрд╡рд░ рдореЗрд▓ рдЦрд╛рддреЗ рд╣реИрдВ, рд╣рдордиреЗ рдирд┐рдореНрди рдлреЙрд░реНрдо рдХрд╛ рдПрдХ рд╕реНрд╡-рд▓рд┐рдЦрд┐рдд рд╡рд░реНрдЧ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд┐рдпрд╛:

рдХреЛрдб рджреЗрдЦреЗрдВ
 using PS.Logs.Unity; /// <summary> /// Compares the same avatar in two states. Compares the values potentially /// affected by prediction. /// </summary> public sealed class GameStateComparer : IGameStateComparer { public bool IsSame(GameState s1, GameState s2, uint avatarId) { if (s1 == null && s2 != null || s1 != null && s2 == null) { return false; } if (s1 == null && s2 == null) return false; var entity1 = s1.WorldState[avatarId]; var entity2 = s2.WorldState[avatarId]; if (entity1 == null && entity2 == null) { return false; } if (entity1 == null || entity2 == null) { LogManager.Debug("entity is different"); return false; } if (s1.Time != s2.Time) { LogManager.Warning(string.Format("Trying to compare states with different time! Predicted time: {0} Server time: {1}", s1.Time, s2.Time)); return false; } if (s1.WorldState.Transform[avatarId] != s2.WorldState.Transform[avatarId]) { LogManager.Debug("Transform is different"); return false; } // ... some code ... return true; } } 


рдпрджрд┐ рдЖрд╡рд╢реНрдпрдХ рд╣реЛ, рддреЛ рдЗрд╕реЗ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рд╣рдордиреЗ рдРрд╕рд╛ рдирд╣реАрдВ рдХрд┐рдпрд╛, рд╣рд╛рд▓рд╛рдВрдХрд┐ рд╣рдордиреЗ рднрд╡рд┐рд╖реНрдп рдореЗрдВ рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╕реЛрдЪрд╛ рдерд╛ред

рд░реВрдкрд╛рдВрддрд░рдг рддреБрд▓рдирд╛ рдХреЛрдб:

рдХреЛрдб рджреЗрдЦреЗрдВ
 public static bool operator ==(Transform a, Transform b) { if ((object)a == null && (object)b == null) { return true; } if ((object)a == null && (object)b != null) { return false; } if ((object)a != null && (object)b == null) { return false; } if (Math.Abs(a.Angle - b.Angle) > 0.01f) { return false; } if (Math.Abs(a.Position.x - b.Position.x) > 0.01f || Math.Abs(a.Position.y - b.Position.y) > 0.01f) { return false; } return true; } 



рдкрд╣рд▓реА рдореБрд╢реНрдХрд┐рд▓реЗрдВ


рдЧрддрд┐ рдХреЗ рд╕рд┐рдореБрд▓реЗрд╢рди рдХреЗ рд╕рд╛рде рдХреЛрдИ рд╕рдорд╕реНрдпрд╛ рдирд╣реАрдВ рдереА, рдЬрдмрдХрд┐ рдЗрд╕реЗ 2 рдбреА рд╡рд┐рдорд╛рди рдкрд░ рдкреНрд░рдХреНрд╖реЗрдкрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рдерд╛ - рдРрд╕реЗ рдорд╛рдорд▓реЛрдВ рдореЗрдВ рднреМрддрд┐рдХреА рдиреЗ рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рдХрд╛рдо рдХрд┐рдпрд╛, рд▓реЗрдХрд┐рди рдПрдХ рдмрд┐рдВрджреБ рдкрд░ рдЧреЗрдо рдбрд┐рдЬрд╛рдЗрдирд░ рдЖрдП рдФрд░ рдХрд╣рд╛: "рд╣рдо рд╣рдердЧреЛрд▓реЗ рдЪрд╛рд╣рддреЗ рд╣реИрдВ!" рдФрд░ рд╣рдордиреЗ рд╕реЛрдЪрд╛ рддрд╛рдХрд┐ рдХреБрдЫ рднреА рди рдмрджрд▓реЗ! , рд╣рд╛рде рдкрд░ рдХреЗрд╡рд▓ 2D рдбреЗрдЯрд╛ рдХреЗ рд╕рд╛рде рднреМрддрд┐рдХ рд╢рд░реАрд░ рдХреА 3 рдбреА рдЙрдбрд╝рд╛рди рдХрд╛ рдЕрдиреБрдХрд░рдг рдХреНрдпреЛрдВ рдирд╣реАрдВ рдХрд┐рдпрд╛ред

рдФрд░ рдЙрдиреНрд╣реЛрдВрдиреЗ рдХреБрдЫ рд╡рд╕реНрддреБрдУрдВ рдХреЗ рд▓рд┐рдП рдКрдВрдЪрд╛рдИ рдХреА рдЕрд╡рдзрд╛рд░рдгрд╛ рдкреЗрд╢ рдХреАред

рд╕рдордп рдХреЗ рд╕рд╛рде рдКрдБрдЪрд╛рдИ рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрди рдХрд╛ рдирд┐рдпрдо рдПрдХ рдкрд░рд┐рддреНрдпрдХреНрдд рдирд┐рдХрд╛рдп рдХреЗ рд▓рд┐рдП рдХреИрд╕рд╛ рджрд┐рдЦрддрд╛ рд╣реИ, рд╡реЗ рдЖрдард╡реАрдВ рдХрдХреНрд╖рд╛ рдореЗрдВ рднреМрддрд┐рдХреА рдХреЗ рдкрд╛рда рд╕реЗ рдЧреБрдЬрд░рддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рдмреИрд▓рд┐рд╕реНрдЯрд┐рдХ рдкрд░ рдирд┐рд░реНрдгрдп рддреБрдЪреНрдЫ рд╣реЛ рдЧрдпрд╛ред рд▓реЗрдХрд┐рди рдЯрдХреНрдХрд░ рдХреЗ рд╕рд╛рде рд╕рдорд╛рдзрд╛рди рдЕрдм рдЗрддрдирд╛ рддреБрдЪреНрдЫ рдирд╣реАрдВ рдерд╛ред рдЖрдЗрдП рдЗрд╕ рдорд╛рдорд▓реЗ рдХреА рдХрд▓реНрдкрдирд╛ рдХрд░реЗрдВ: рдЙрдбрд╝рд╛рди рдХреЗ рджреМрд░рд╛рди рдПрдХ рдЧреНрд░реЗрдиреЗрдб рдпрд╛ рддреЛ рдПрдХ рджреАрд╡рд╛рд░ рд╕реЗ рдЯрдХрд░рд╛ рдЬрд╛рдП, рдпрд╛ рдЙрд╕рдХреА рд╡рд░реНрддрдорд╛рди рдКрдВрдЪрд╛рдИ рдФрд░ рджреАрд╡рд╛рд░ рдХреА рдКрдВрдЪрд╛рдИ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдЙрд╕ рдкрд░ рдЙрдбрд╝рд╛рди рднрд░ рдЬрд╛рдПред рд╣рдо рд╕рдорд╕реНрдпрд╛ рдХреЛ рдХреЗрд╡рд▓ рджреЛ-рдЖрдпрд╛рдореА рджреБрдирд┐рдпрд╛ рдореЗрдВ рд╣рд▓ рдХрд░реЗрдВрдЧреЗ рдЬрд╣рд╛рдВ рдПрдХ рд╕рд░реНрдХрд▓ рджреНрд╡рд╛рд░рд╛ рдЧреНрд░реЗрдиреЗрдб рдХреЛ рдПрдХ рд╕рд░реНрдХрд▓ рдФрд░ рджреАрд╡рд╛рд░ рджреНрд╡рд╛рд░рд╛ рджрд░реНрд╢рд╛рдпрд╛ рдЧрдпрд╛ рд╣реИред


рд╕рдорд╕реНрдпрд╛ рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╡рд╕реНрддреБрдУрдВ рдХреА рдЬреНрдпрд╛рдорд┐рддрд┐ рдХрд╛ рджреГрд╢реНрдпред

рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рд╣рдордиреЗ рдЕрдиреНрдп рд╕реНрдерд┐рд░ рдФрд░ рдЧрддрд┐рд╢реАрд▓ рдирд┐рдХрд╛рдпреЛрдВ рдХреЗ рд╕рд╛рде рдПрдХ рдЧреНрд░реЗрдиреЗрдб рдХреЗ рдЧрддрд┐рд╢реАрд▓ рд╢рд░реАрд░ рдХреА рдмрд╛рддрдЪреАрдд рдХреЛ рдмрдВрдж рдХрд░ рджрд┐рдпрд╛ред рд▓рдХреНрд╖реНрдп рдкрд░ рдзреНрдпрд╛рди рдХреЗрдВрджреНрд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдпрд╣ рдЖрд╡рд╢реНрдпрдХ рд╣реИред рд╣рдорд╛рд░реЗ рдХрд╛рд░реНрдп рдореЗрдВ, рдПрдХ рдЧреНрд░реЗрдиреЗрдб рдХреЛ рдЕрдиреНрдп рд╡рд╕реНрддреБрдУрдВ рд╕реЗ рдЧреБрдЬрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП рдФрд░ рдПрдХ рджреАрд╡рд╛рд░ рдкрд░ "рдЙрдбрд╝рдирд╛" рдЪрд╛рд╣рд┐рдП рдЬрдм рджреЛ-рдЖрдпрд╛рдореА рд╡рд┐рдорд╛рди рдкрд░ рдЙрдирдХреЗ рдЕрдиреБрдорд╛рди рдПрдХ рджреВрд╕рд░реЗ рдХреЛ рдХрд╛рдЯрддреЗ рд╣реИрдВред рдПрдХ рд╕рд╛рдорд╛рдиреНрдп рдмрд╛рддрдЪреАрдд рдореЗрдВ, рджреЛ рд╡рд╕реНрддреБрдПрдВ рдПрдХ рджреВрд╕рд░реЗ рд╕реЗ рдирд╣реАрдВ рдЧреБрдЬрд░ рд╕рдХрддреА рд╣реИрдВ, рдФрд░ рдлрд┐рд░ рднреА рдПрдХ рдХрд╕реНрдЯрдо рдЖрдВрджреЛрд▓рди рддрд░реНрдХ рдФрд░ рдКрдВрдЪрд╛рдИ рдХреЗ рд╕рд╛рде рдЧреНрд░реЗрдиреЗрдб рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╣рдордиреЗ рдЗрд╕реЗ рдХреБрдЫ рд╢рд░реНрддреЛрдВ рдХреЗ рддрд╣рдд рдРрд╕рд╛ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреАред

рд╣рдордиреЗ рдЧреНрд░реЗрдиреЗрдб рдХреЗ рд▓рд┐рдП рдПрдХ рдЕрд▓рдЧ рдШрдЯрдХ GrenadeMovement рдХреА рд╢реБрд░реБрдЖрдд рдХреА, рдЬрд┐рд╕рдореЗрдВ рд╣рдордиреЗ рдКрдВрдЪрд╛рдИ рдХреА рдЕрд╡рдзрд╛рд░рдгрд╛ рдкреЗрд╢ рдХреА:

 [Component] public class GrenadeMovement { public float Height; [DontPack] public Vector2 Velocity; [DontPack] public float VerticalVelocity; public GrenadeMovement(float height, Vector2 velocity, float verticalVelocity) { } } 

рдЕрдм рдЧреНрд░реЗрдиреЗрдб рдХрд╛ рдПрдХ рдКрдВрдЪрд╛рдИ рд╕рдордиреНрд╡рдп рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рдЬрд╛рдирдХрд╛рд░реА рдмрд╛рдХреА рджреБрдирд┐рдпрд╛ рдХреЛ рдХреБрдЫ рдирд╣реАрдВ рджреЗрддреА рд╣реИред рдЗрд╕рд▓рд┐рдП, рд╣рдордиреЗ рдзреЛрдЦрд╛ рджреЗрдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛ рдФрд░ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╢рд░реНрдд рдХреЛ рдЬреЛрдбрд╝рд╛: рдПрдХ рдЧреНрд░реЗрдиреЗрдб рджреАрд╡рд╛рд░реЛрдВ рдкрд░ рдЙрдбрд╝ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдХреЗрд╡рд▓ рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рдКрдВрдЪрд╛рдИ рддрдХред рдЗрд╕ рдкреНрд░рдХрд╛рд░, рдЯрдХрд░рд╛рд╡ рдХреА рдкреВрд░реА рдкрд░рд┐рднрд╛рд╖рд╛ рдкреНрд░рдХреНрд╖реЗрдкрдг рдЯрдХреНрдХрд░реЛрдВ рдХреА рдЬрд╛рдБрдЪ рдХрд░рдиреЗ рдФрд░ рдЧреНрд░реЗрдиреЗрдбреИрдореЛрд╡рдореЗрдВрдЯ рдХреЗ рдореВрд▓реНрдп рдХреЗ рд╕рд╛рде рджреАрд╡рд╛рд░ рдХреА рдКрдВрдЪрд╛рдИ рдХреА рддреБрд▓рдирд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдиреАрдЪреЗ рдЖрдИред рд╣рд╛рдЗрдЯ рдлрд╝реАрд▓реНрдбред рдпрджрд┐ рдЧреНрд░реЗрдиреЗрдб рдХреА рдЙрдбрд╝рд╛рди рдХреА рдКрдВрдЪрд╛рдИ рдХрдо рд╣реИ, рддреЛ рдпрд╣ рджреАрд╡рд╛рд░ рд╕реЗ рдЯрдХрд░рд╛рддрд╛ рд╣реИ, рдЕрдиреНрдпрдерд╛ рдпрд╣ рд╢рд╛рдВрддрд┐ рд╕реЗ рдЕрдкрдиреЗ рдкрде рдХреЗ рд╕рд╛рде рдЖрдЧреЗ рдмрдврд╝рдирд╛ рдЬрд╛рд░реА рд░рдЦ рд╕рдХрддрд╛ рд╣реИ, рдЬрд┐рд╕рдореЗрдВ 2D рд╕реНрдкреЗрд╕ рднреА рд╢рд╛рдорд┐рд▓ рд╣реИред

рдкрд╣рд▓реЗ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рдореЗрдВ, рдЪреМрд░рд╛рд╣реЛрдВ рдХреЛ рдЦреЛрдЬрдиреЗ рдХреЗ рджреМрд░рд╛рди рдЧреНрд░реЗрдиреЗрдб рдмрд╕ рдЧрд┐рд░ рдЧрдпрд╛, рд▓реЗрдХрд┐рди рдлрд┐рд░ рд╣рдордиреЗ рд▓реЛрдЪрджрд╛рд░ рдЯрдХрд░рд╛рд╡реЛрдВ рдХреЛ рдЬреЛрдбрд╝рд╛, рдФрд░ рдпрд╣ рдЙрд╕ рдкрд░рд┐рдгрд╛рдо рд╕реЗ рд▓рдЧрднрдЧ рдЕрдкреНрд░рднреЗрджреНрдп рд╡реНрдпрд╡рд╣рд╛рд░ рдХрд░рдиреЗ рд▓рдЧрд╛, рдЬреЛ рд╣рдореЗрдВ 3 рдбреА рдореЗрдВ рдорд┐рд▓реЗрдЧрд╛ред

рдПрдХ рдЧреНрд░реЗрдиреЗрдб рдФрд░ рд▓реЛрдЪрджрд╛рд░ рдЯрдХрд░рд╛рд╡ рдХреЗ рдкреНрд░рдХреНрд╖реЗрдкрд╡рдХреНрд░ рдХреА рдЧрдгрдирд╛ рдХреЗ рд▓рд┐рдП рдкреВрд░рд╛ рдХреЛрдб рдиреАрдЪреЗ рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ:

рдХреЛрдб рджреЗрдЦреЗрдВ
 using System; // ... some code ... using Volatile; namespace Common.WorldState { public sealed class GrenadeMovementSystem : ExecutableSystem { private struct Projection { public float Min; public float Max; } private float _r; private readonly Vector2[] _vertices = new Vector2[4]; private readonly Vector2[] _verticesV = new Vector2[4]; private Vector2 _Vunit; private Vector2 _VTunit; private Projection _wallProj1; private Projection _wallProj2; private Projection _wallProj1V; private Projection _wallProj2V; private const float CollisionPrecision = 1e-3f; private static readonly float HalfSlope = Mathf.Cos(Mathf.PI / 4.0f); private readonly ContactPointList _contactPoints = new ContactPointList(3); public override void Execute(GameState gs) { var settings = gs.RuleBook.GrenadeConfig[1]; _r = settings.R; var floorDampeningPerTick = (float)Math.Pow(settings.FloorDampening, 1.0 / GameState.Hz); foreach (var grenade in gs.WorldState.GrenadeMovement) { // Gravity must take effect before collision // because contact with walls may and will adjust vertical velocity // and penetration will even move the ball up. grenade.Value.VerticalVelocity -= settings.Gravity * GameState.TickDurationSec; grenade.Value.Height += grenade.Value.VerticalVelocity * GameState.TickDurationSec; // prevent falling through floor if (grenade.Value.Height <= _r) { // slow down horizontal movement by floor friction // actually, friciton is simplified to just dampening coefficient var spdH = grenade.Value.Velocity.sqrMagnitude; var spdV = grenade.Value.VerticalVelocity; var cos = spdH / Mathf.Sqrt(spdH * spdH + spdV * spdV); grenade.Value.Velocity *= floorDampeningPerTick * cos; // slow down vertical movement grenade.Value.VerticalVelocity = settings.FloorRestitution * Math.Abs(grenade.Value.VerticalVelocity); // move up to the floor level grenade.Value.Height = _r; } // A collision will stop the ball and change its velocity. // Otherwise it will be moved by velocity PerformCollisionAndMovement(gs, grenade.Key, grenade.Value); } } private void PerformCollisionAndMovement(GameState gs, uint id, GrenadeMovement grenade) { var settings = gs.RuleBook.GrenadeConfig[1]; var velocity = grenade.Velocity * GameState.TickDurationSec; var trans = gs.WorldState.Transform[id]; var position = trans.Position; _Vunit = velocity.normalized; _VTunit = new Vector2(-_Vunit.y, _Vunit.x); _vertices[0] = position + _VTunit * _r; _vertices[1] = position - _VTunit * _r; _vertices[2] = _vertices[1] + velocity; _vertices[3] = _vertices[0] + velocity; _contactPoints.Reset(); int collisions = 0; var grenProj1V = ProjectCapsule(_Vunit, _vertices, position, velocity); var grenProj2V = ProjectCapsule(_VTunit, _vertices, position, velocity); collisions += CollideWithStaticBoxes(gs, id, position, velocity, grenade, grenProj1V, grenProj2V); collisions += CollideWithCircles(gs, gs.RuleBook.StaticCircleCollider, gs.RuleBook.Transform, id, position, velocity, grenade, grenProj1V, grenProj2V, (CollisionLayer)~0); collisions += CollideWithCircles(gs, gs.WorldState.DynamicCircleCollider, gs.WorldState.Transform, id, position, velocity, grenade, grenProj1V, grenProj2V, ~CollisionLayer.Character); if (collisions == 0) { trans.Position += velocity; } else { var contactSuperposition = CalculateContactSuperposition(); trans.Position += velocity * contactSuperposition.TravelDistance; var reflectedVelocity = grenade.Velocity - 2.0f * Vector2.Dot(grenade.Velocity, contactSuperposition.Normal) * contactSuperposition.Normal; reflectedVelocity *= settings.WallRestitution; #if DEBUG_GRENADES gs.Log.Debug("contact" + "\n\ttravel " + contactSuperposition.TravelDistance + "\n\tcontactNormal " + contactSuperposition.Normal.x + ":" + contactSuperposition.Normal.y + "\n\treflected V " + reflectedVelocity.x + ":" + reflectedVelocity.y); #endif grenade.Velocity = reflectedVelocity; } } private int CollideWithStaticBoxes( GameState gs, uint id, Vector2 position, Vector2 velocity, GrenadeMovement grenade, Projection grenProj1V, Projection grenProj2V) { var settings = gs.RuleBook.GrenadeConfig[1]; var collisions = 0; // TODO spatial query foreach (var collider in gs.RuleBook.StaticBoxCollider) { var wall = collider.Value; var transform = gs.RuleBook.Transform[collider.Key]; var colliderData = gs.RuleBook.PrecomputedColliderData[collider.Key]; // test projection to V _wallProj1V = ProjectPolygon(_Vunit, colliderData.Vertices); if (!Overlap(_wallProj1V, grenProj1V)) continue; // test projection to VT _wallProj2V = ProjectPolygon(_VTunit, colliderData.Vertices); if (!Overlap(_wallProj2V, grenProj2V)) continue; // test projection to wall axis 1 _wallProj1 = ProjectPolygon(colliderData.Axis1, colliderData.Vertices); var grenProj1 = ProjectCapsule(colliderData.Axis1, _vertices, position, velocity); if (!Overlap(_wallProj1, grenProj1)) continue; // test projection to wall axis 2 _wallProj2 = ProjectPolygon(colliderData.Axis2, colliderData.Vertices); var grenProj2 = ProjectCapsule(colliderData.Axis2, _vertices, position, velocity); if (!Overlap(_wallProj2, grenProj2)) continue; var lowWall = wall.Height < settings.TallWallHeight; if (lowWall) { // the wall is too far below, ignore it completely if (grenade.Height > wall.Height + _r) continue; // if grenade if falling down, it can bounce off the top of the wall if (grenade.VerticalVelocity < 0f) { if (grenade.Height > wall.Height - _r) { var localPV = WorldToBoxLocal(transform.Position, colliderData, position + velocity); #if DEBUG_GRENADES gs.Log.Debug("fall on wall" + "\n\tP+V " + (Px + Vx) + ":" + (Py + Vy) + "\n\tlocal " + localPV.x + ":" + localPV.y + "\n\tH w " + wall.Height + " g " + grenade.Height ); #endif if (Math.Abs(localPV.x) < wall.Size.x * 0.5f || Math.Abs(localPV.y) < wall.Size.y * 0.5f) { grenade.Height = wall.Height + _r; grenade.VerticalVelocity = settings.WallRestitution * Math.Abs(grenade.VerticalVelocity); continue; } } } } // collision detected // try to find minimal V before collision var scaleV = CalcTranslationScaleBeforeCollision(CheckBoxCollision, colliderData, 0, position, velocity); var contactPoint = CalcBoxContactPoint(transform.Position, wall, colliderData, position); #if DEBUG_GRENADES gs.Log.Debug("collision grenade #" + id + " with static box #" + collider.Key + "\n\tP=" + Px + ":" + Py + "\n\tV=" + Vx + ":" + Vy + " scale=" + scaleV + "\n\tP+Vs=" + (Px + Vx * scaleV) + ":" + (Py + Vy * scaleV) + "\n\twall pos " + transform.Position.x + ":" + transform.Position.y + " sz " + wall.Size.x + ":" + wall.Size.y + " angle " + transform.Angle + "\n\tproj V w " + _wallProj1V.Min + ":" + _wallProj1V.Max + " g " + grenProj1V.Min + ":" + grenProj1V.Max + " overlap=" + Overlap(_wallProj1V, grenProj1V) + "\n\tproj VT w " + _wallProj2V.Min + ":" + _wallProj2V.Max + " g " + grenProj2V.Min + ":" + grenProj2V.Max + " overlap=" + Overlap(_wallProj2V, grenProj2V) + "\n\taxis1 " + colliderData.Axis1.x + ":" + colliderData.Axis1.y + "\n\tproj 1 w " + _wallProj1.Min + ":" + _wallProj1.Max + " g " + grenProj1.Min + ":" + grenProj1.Max + " overlap=" + Overlap(_wallProj1, grenProj1) + "\n\taxis2 " + colliderData.Axis2.x + ":" + colliderData.Axis2.y + "\n\tproj 2 w " + _wallProj2.Min + ":" + _wallProj2.Max + " g " + grenProj2.Min + ":" + grenProj2.Max + " overlap=" + Overlap(_wallProj2, grenProj2) + "\n\tpoint " + contactPoint.Point.x + ":" + contactPoint.Point.y + " dotV " + Vector2.Dot(P - contactPoint.Point, V) ); #endif // ignore colliders that are behind if (Vector2.Dot(position - contactPoint.Point, velocity) >= 0.0f) continue; contactPoint.TravelDistance = velocity.magnitude * scaleV; _contactPoints.Add(ref contactPoint); collisions++; } return collisions; } private bool CheckBoxCollision(PrecomputedColliderData colliderData, int x, Vector2 position, Vector2 velocity) { _verticesV[0] = _vertices[0]; _verticesV[1] = _vertices[1]; _verticesV[2] = _vertices[1] + velocity; _verticesV[3] = _vertices[0] + velocity; // test projection to V var grenProj1V = ProjectCapsule(_Vunit, _verticesV, position, velocity); if (!Overlap(_wallProj1V, grenProj1V)) return false; // testing projection to VT would be redundant // test projection to wall axis 1 var grenProj1 = ProjectCapsule(colliderData.Axis1, _verticesV, position, velocity); if (!Overlap(_wallProj1, grenProj1)) return false; // test projection to wall axis 2 var grenProj2 = ProjectCapsule(colliderData.Axis2, _verticesV, position, velocity); if (!Overlap(_wallProj2, grenProj2)) return false; return true; } private int CollideWithCircles( GameState gs, Table<CircleCollider> colliderTable, Table<Transform> transformTable, uint id, Vector2 position, Vector2 velocity, GrenadeMovement grenade, Projection grenProj1V, Projection grenProj2V, CollisionLayer collisionLayers) { var settings = gs.RuleBook.GrenadeConfig[1]; var collisions = 0; foreach (var collider in colliderTable) { if ((int)collisionLayers != ~0) { var body = gs.WorldState.PhysicsDynamicBody[collider.Key]; if (body != null && (body.CollisionLayer & collisionLayers) == 0) continue; } var wall = collider.Value; var transform = transformTable[collider.Key]; // test projection to V _wallProj1V = ProjectCircle(_Vunit, transform.Position, wall.Radius); if (!Overlap(_wallProj1V, grenProj1V)) continue; // test projection to VT _wallProj2V = ProjectCircle(_VTunit, transform.Position, wall.Radius); if (!Overlap(_wallProj2V, grenProj2V)) continue; // test distance from the circle wall to semicircles on capsule ends var collisionDistance = (_r + wall.Radius) * (_r + wall.Radius); if ((position - transform.Position).sqrMagnitude > collisionDistance) continue; var distSqr = (position + velocity - transform.Position).sqrMagnitude; if (distSqr > collisionDistance) continue; var lowWall = wall.Height < settings.TallWallHeight; if (lowWall) { // the wall is too far below, ignore it completely if (grenade.Height > wall.Height + _r) continue; // if grenade if falling down, it can bounce off the top of the wall if (grenade.VerticalVelocity < 0f) { if (grenade.Height > wall.Height - _r) { #if DEBUG_GRENADES gs.Log.Debug("grenade #" + id + " falls on wall" + "\n\tP+V " + (Px + Vx) + ":" + (Py + Vy) + "\n\tdist " + Mathf.Sqrt(distSqr) + "\n\tH w " + wall.Height + " g " + grenade.Height ); #endif if (distSqr < wall.Radius * wall.Radius) { grenade.Height = wall.Height + _r; grenade.VerticalVelocity = settings.WallRestitution * Math.Abs(grenade.VerticalVelocity); continue; } } } } // collision detected // try to find minimal V before collision var scaleV = CalcTranslationScaleBeforeCollision(CheckCircleCollision, transform.Position, wall, position, velocity); var contactPoint = CalcCircleContactPoint(transform.Position, wall, position); #if DEBUG_GRENADES gs.Log.Debug("collision grenade #" + id + " with circle #" + collider.Key + "\n\tP=" + Px + ":" + Py + "\n\tV=" + Vx + ":" + Vy + " scale=" + scaleV + "\n\tP+Vs=" + (Px + Vx * scaleV) + ":" + (Py + Vy * scaleV) + "\n\tcircle pos " + transform.Position.x + ":" + transform.Position.y + " r " + wall.Radius + "\n\tdist " + (transform.Position - (P + V * scaleV)).magnitude + "\n\tproj V w " + _wallProj1V.Min + ":" + _wallProj1V.Max + " g " + grenProj1V.Min + ":" + grenProj1V.Max + " overlap=" + Overlap(_wallProj1V, grenProj1V) + "\n\tproj VT w " + _wallProj2V.Min + ":" + _wallProj2V.Max + " g " + grenProj2V.Min + ":" + grenProj2V.Max + " overlap=" + Overlap(_wallProj2V, grenProj2V) + "\n\tpoint " + contactPoint.Point.x + ":" + contactPoint.Point.y + " dotV " + Vector2.Dot(P - contactPoint.Point, V) ); #endif // ignore colliders that are behind if (Vector2.Dot(position - contactPoint.Point, velocity) >= 0.0f) continue; contactPoint.TravelDistance = velocity.magnitude * scaleV; _contactPoints.Add(ref contactPoint); collisions++; } return collisions; } private bool CheckCircleCollision(Vector2 wallCentre, CircleCollider wall, Vector2 position, Vector2 velocity) { _verticesV[0] = _vertices[0]; _verticesV[1] = _vertices[1]; _verticesV[2] = _vertices[1] + velocity; _verticesV[3] = _vertices[0] + velocity; // test projection to V var grenProj1V = ProjectCapsule(_Vunit, _verticesV, position, velocity); if (!Overlap(_wallProj1V, grenProj1V)) return false; // testing projection to VT would be redundant // test distance from the circle wall to the semicircle on the second capsule end var dSqr = (_r + wall.Radius) * (_r + wall.Radius); return (position + velocity - wallCentre).sqrMagnitude < dSqr; } private static float CalcTranslationScaleBeforeCollision<TData1, TData2>( Func<TData1, TData2, Vector2, Vector2, bool> collision, TData1 colliderData1, TData2 colliderData2, Vector2 position, Vector2 vector) { var min = 0.0f; var max = 1.0f; while (true) { var d = (max - min) * 0.5f; if (d < CollisionPrecision) break; var scale = min + d; if (collision(colliderData1, colliderData2, position, vector * scale)) { max = scale; } else { min = scale; } } return min; } private ContactPoint CalculateContactSuperposition() { ContactPoint contactSuperposition; _contactPoints.TryPopClosest(1000f, out contactSuperposition); ContactPoint contact; while (_contactPoints.TryPopClosest(contactSuperposition.TravelDistance, out contact)) { contactSuperposition.Normal += contact.Normal; } contactSuperposition.Normal = contactSuperposition.Normal.normalized; return contactSuperposition; } private static Projection ProjectPolygon(Vector2 axisNormalised, Vector2[] vertices) { Projection proj; var d = Vector2.Dot(axisNormalised, vertices[0]); proj.Min = d; proj.Max = d; for (var i = 1; i < vertices.Length; i++) { d = Vector2.Dot(axisNormalised, vertices[i]); proj.Min = Mathf.Min(proj.Min, d); proj.Max = Mathf.Max(proj.Max, d); } return proj; } private Projection ProjectCapsule(Vector2 axisNormalised, Vector2[] vertices, Vector2 p, Vector2 v) { var proj = ProjectPolygon(axisNormalised, vertices); proj = AddCircleProjection(proj, axisNormalised, p, _r); proj = AddCircleProjection(proj, axisNormalised, p + v, _r); return proj; } private static Projection AddCircleProjection(Projection proj, Vector2 axisNormalised, Vector2 centre, float r) { var c = Vector2.Dot(axisNormalised, centre); proj.Min = Mathf.Min(proj.Min, c - r); proj.Max = Mathf.Max(proj.Max, c + r); return proj; } private static Projection ProjectCircle(Vector2 axisNormalised, Vector2 centre, float r) { Projection proj; var c = Vector2.Dot(axisNormalised, centre); proj.Min = c - r; proj.Max = c + r; return proj; } private static bool Overlap(Projection p1, Projection p2) { return p1.Min < p2.Min ? p1.Max > p2.Min : p2.Max > p1.Min; } private static Vector2 WorldToBoxLocal(Vector2 wallCentre, PrecomputedColliderData colliderData, Vector2 position) { return new Vector2( Vector2.Dot(colliderData.Axis1, position) - Vector2.Dot(colliderData.Axis1, wallCentre), Vector2.Dot(colliderData.Axis2, position) - Vector2.Dot(colliderData.Axis2, wallCentre) ); } private static ContactPoint CalcBoxContactPoint(Vector2 wallCentre, BoxCollider wall, PrecomputedColliderData colliderData, Vector2 position) { var contactPoint = CaclBoxLocalContactPoint(wall.Size * 0.5f, WorldToBoxLocal(wallCentre, colliderData, position)); var worldAxisX = new Vector2(colliderData.Axis1.x, -colliderData.Axis1.y); var worldAxisY = new Vector2(colliderData.Axis1.y, colliderData.Axis1.x); contactPoint.Point = wallCentre + new Vector2(Vector2.Dot(worldAxisX, contactPoint.Point), Vector2.Dot(worldAxisY, contactPoint.Point)); contactPoint.Normal = new Vector2(Vector2.Dot(worldAxisX, contactPoint.Normal), Vector2.Dot(worldAxisY, contactPoint.Normal)); return contactPoint; } private static ContactPoint CaclBoxLocalContactPoint(Vector2 boxHalfSize, Vector2 localPosition) { ContactPoint localContactPoint = default(ContactPoint); // cases are numbered like numpad keys // 1, 2, 3 if (localPosition.y < -boxHalfSize.y) { // 1 if (localPosition.x < -boxHalfSize.x) { localContactPoint.Point = new Vector2(-boxHalfSize.x, -boxHalfSize.y); localContactPoint.Normal = new Vector2(-HalfSlope, -HalfSlope); } // 2, 3 else { // 3 if (localPosition.x > boxHalfSize.x) { localContactPoint.Point = new Vector2(boxHalfSize.x, -boxHalfSize.y); localContactPoint.Normal = new Vector2(HalfSlope, -HalfSlope); } // 2 else { localContactPoint.Point = new Vector2(localPosition.x, -boxHalfSize.y); localContactPoint.Normal = new Vector2(0.0f, -1.0f); } } } // 4, 6, 7, 8, 9 else { // 7, 8, 9 if (localPosition.y > boxHalfSize.y) { // 7 if (localPosition.x < -boxHalfSize.x) { localContactPoint.Point = new Vector2(-boxHalfSize.x, boxHalfSize.y); localContactPoint.Normal = new Vector2(-HalfSlope, HalfSlope); } // 8, 9 else { // 9 if (localPosition.x > boxHalfSize.x) { localContactPoint.Point = new Vector2(boxHalfSize.x, boxHalfSize.y); localContactPoint.Normal = new Vector2(HalfSlope, HalfSlope); } // 8 else { localContactPoint.Point = new Vector2(localPosition.x, boxHalfSize.y); localContactPoint.Normal = new Vector2(0.0f, 1.0f); } } } // 4, 6 else { // 4 if (localPosition.x < -boxHalfSize.x) { localContactPoint.Point = new Vector2(-boxHalfSize.x, localPosition.y); localContactPoint.Normal = new Vector2(-1.0f, 0.0f); } // 6 else { localContactPoint.Point = new Vector2(boxHalfSize.x, localPosition.y); localContactPoint.Normal = new Vector2(1.0f, 0.0f); } } } return localContactPoint; } private static ContactPoint CalcCircleContactPoint(Vector2 wallCentre, CircleCollider wall, Vector2 position) { ContactPoint contactPoint = default(ContactPoint); contactPoint.Normal = (position - wallCentre).normalized; contactPoint.Point = wallCentre + wall.Radius * contactPoint.Normal; return contactPoint; } } } 


. рдЖрдЧреЗ рдХреНрдпрд╛ рд╣реИ?


, , - , . ECS . , , JSON, ECS. :



, ┬л┬╗. ECS, , . тАХ тАХ , , ECS, ECS . , API, , , . , .

- 2D-: , . , : , opensource , - . ECS, , . , , . - , , . тАХ - .

- , 3D-, , .

, , , . , , ECS .

рдЙрдкрдпреЛрдЧреА рд▓рд┐рдВрдХ


:


:

Source: https://habr.com/ru/post/hi481880/


All Articles