|
|
|
HULL_POLYGON = [ |
|
[-20, +12], [+6, +12], |
|
[+15, +4], [+15, -4], |
|
[+6, -12], [-20, -12] |
|
]; |
|
|
|
BODY_P1 = [ |
|
[-8, +9], [+8, +12], |
|
[+8, -12], [-8, -9] |
|
]; |
|
|
|
BODY_P2 = [ |
|
[-8, +4], [+8, +9], |
|
[+8, -9], [-8, -4] |
|
]; |
|
|
|
|
|
BODY_P3 = [ |
|
[-4, +2], [+4, +4], |
|
[+4, -4], [-4, -2] |
|
]; |
|
|
|
FIN = [ |
|
[-1, -10], [-1, +10], |
|
[+1, +10], [+1, -10] |
|
]; |
|
|
|
HULL_BOTTOM_WIDTH = 35; |
|
const SPEED = 6; |
|
|
|
|
|
|
|
|
|
class FishBody extends SwimmerAbstractBody { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor(scale, motors_torque=80, density, nb_steps_outside_water=600) { |
|
super(scale, motors_torque, density, nb_steps_outside_water); |
|
this.TORQUE_PENALTY = 0.00035; |
|
|
|
this.AGENT_WIDTH = HULL_BOTTOM_WIDTH / this.SCALE; |
|
this.AGENT_HEIGHT = 18 / this.SCALE; |
|
this.AGENT_CENTER_HEIGHT = 9 / this.SCALE; |
|
|
|
this.remove_reward_on_head_angle = true; |
|
|
|
this.fins = []; |
|
this.tail = null; |
|
} |
|
|
|
draw(world, init_x, init_y){ |
|
|
|
let vertices; |
|
let rjd; |
|
let joint_motor; |
|
|
|
|
|
let hull_fd = new b2.FixtureDef(); |
|
hull_fd.shape = new b2.PolygonShape(); |
|
vertices = []; |
|
for(let vertex of HULL_POLYGON){ |
|
vertices.push(new b2.Vec2(vertex[0] / this.SCALE, vertex[1] / this.SCALE)); |
|
} |
|
hull_fd.shape.Set(vertices, HULL_POLYGON.length); |
|
hull_fd.density = this.DENSITY; |
|
hull_fd.friction = 0.1; |
|
hull_fd.filter.categoryBits = 0x20; |
|
hull_fd.filter.maskBits = 0x000F; |
|
|
|
let hull_bd = new b2.BodyDef(); |
|
hull_bd.type = b2.Body.b2_dynamicBody; |
|
hull_bd.position.Set(init_x, init_y); |
|
let hull = world.CreateBody(hull_bd); |
|
hull.CreateFixture(hull_fd); |
|
hull.color1 = "#806682"; |
|
hull.color2 = "#4D4D80"; |
|
hull.SetUserData(new CustomBodyUserData(true, false, "head")); |
|
this.body_parts.push(hull); |
|
this.reference_head_object = hull; |
|
|
|
|
|
let body_p1_x = init_x - 35 / 2 / this.SCALE - 16 / 2 / this.SCALE; |
|
let body_p1_fd = new b2.FixtureDef(); |
|
body_p1_fd.shape = new b2.PolygonShape(); |
|
vertices = []; |
|
for(let vertex of BODY_P1){ |
|
vertices.push(new b2.Vec2(vertex[0] / this.SCALE, vertex[1] / this.SCALE)); |
|
} |
|
body_p1_fd.shape.Set(vertices, BODY_P1.length); |
|
body_p1_fd.density = this.DENSITY; |
|
body_p1_fd.restitution = 0.0; |
|
body_p1_fd.filter.categoryBits = 0x20; |
|
body_p1_fd.filter.maskBits = 0x000F; |
|
|
|
let body_p1_bd = new b2.BodyDef(); |
|
body_p1_bd.type = b2.Body.b2_dynamicBody; |
|
body_p1_bd.position.Set(body_p1_x, init_y); |
|
let body_p1 = world.CreateBody(body_p1_bd); |
|
body_p1.CreateFixture(body_p1_fd); |
|
body_p1.color1 = "#806682"; |
|
body_p1.color2 = "#4D4D80"; |
|
body_p1.SetUserData(new CustomBodyUserData(true, false, "body")); |
|
this.body_parts.push(body_p1); |
|
|
|
|
|
rjd = new b2.RevoluteJointDef(); |
|
rjd.Initialize(hull, body_p1, new b2.Vec2(init_x - 35 / 2 / this.SCALE, init_y)); |
|
rjd.enableMotor = true; |
|
rjd.enableLimit = true; |
|
rjd.maxMotorTorque = this.MOTORS_TORQUE; |
|
rjd.motorSpeed = 1; |
|
rjd.lowerAngle = -0.1 * Math.PI; |
|
rjd.upperAngle = 0.2 * Math.PI; |
|
joint_motor = world.CreateJoint(rjd); |
|
joint_motor.SetUserData(new CustomMotorUserData("neck", SPEED, true, 0.0, body_p1)); |
|
this.motors.push(joint_motor); |
|
|
|
|
|
let body_p2_x = body_p1_x - 16 / 2 / this.SCALE - 16 / 2 / this.SCALE; |
|
let body_p2_fd = new b2.FixtureDef(); |
|
body_p2_fd.shape = new b2.PolygonShape(); |
|
vertices = []; |
|
for(let vertex of BODY_P2){ |
|
vertices.push(new b2.Vec2(vertex[0] / this.SCALE, vertex[1] / this.SCALE)); |
|
} |
|
body_p2_fd.shape.Set(vertices, BODY_P2.length); |
|
body_p2_fd.density = this.DENSITY; |
|
body_p2_fd.restitution = 0.0; |
|
body_p2_fd.filter.categoryBits = 0x20; |
|
body_p2_fd.filter.maskBits = 0x000F; |
|
|
|
let body_p2_bd = new b2.BodyDef(); |
|
body_p2_bd.type = b2.Body.b2_dynamicBody; |
|
body_p2_bd.position.Set(body_p2_x, init_y); |
|
let body_p2 = world.CreateBody(body_p2_bd); |
|
body_p2.CreateFixture(body_p2_fd); |
|
body_p2.color1 = "#806682"; |
|
body_p2.color2 = "#4D4D80"; |
|
body_p2.SetUserData(new CustomBodyUserData(true, false, "body")); |
|
this.body_parts.push(body_p2); |
|
|
|
|
|
rjd = new b2.RevoluteJointDef(); |
|
rjd.Initialize(body_p1, body_p2, new b2.Vec2(body_p1_x - 16 / 2 / this.SCALE, init_y)); |
|
rjd.enableMotor = true; |
|
rjd.enableLimit = true; |
|
rjd.maxMotorTorque = this.MOTORS_TORQUE; |
|
rjd.motorSpeed = 1; |
|
rjd.lowerAngle = -0.15 * Math.PI; |
|
rjd.upperAngle = 0.15 * Math.PI; |
|
joint_motor = world.CreateJoint(rjd); |
|
joint_motor.SetUserData(new CustomMotorUserData("hip", SPEED, true, 0.0, body_p2)); |
|
this.motors.push(joint_motor); |
|
|
|
|
|
let body_p3_x = body_p2_x - 16 / 2 / this.SCALE - 8 / 2 / this.SCALE; |
|
let body_p3_fd = new b2.FixtureDef(); |
|
body_p3_fd.shape = new b2.PolygonShape(); |
|
vertices = []; |
|
for(let vertex of BODY_P3){ |
|
vertices.push(new b2.Vec2(vertex[0] / this.SCALE, vertex[1] / this.SCALE)); |
|
} |
|
body_p3_fd.shape.Set(vertices, BODY_P3.length); |
|
body_p3_fd.density = this.DENSITY; |
|
body_p3_fd.restitution = 0.0; |
|
body_p3_fd.filter.categoryBits = 0x20; |
|
body_p3_fd.filter.maskBits = 0x000F; |
|
|
|
let body_p3_bd = new b2.BodyDef(); |
|
body_p3_bd.type = b2.Body.b2_dynamicBody; |
|
body_p3_bd.position.Set(body_p3_x, init_y); |
|
let body_p3 = world.CreateBody(body_p3_bd); |
|
body_p3.CreateFixture(body_p3_fd); |
|
body_p3.color1 = "#806682"; |
|
body_p3.color2 = "#4D4D80"; |
|
body_p3.SetUserData(new CustomBodyUserData(true, false, "body")); |
|
this.body_parts.push(body_p3); |
|
this.tail = body_p3; |
|
|
|
|
|
rjd = new b2.RevoluteJointDef(); |
|
rjd.Initialize(body_p2, body_p3, new b2.Vec2(body_p2_x - 16 / 2 / this.SCALE, init_y)); |
|
rjd.enableMotor = true; |
|
rjd.enableLimit = true; |
|
rjd.maxMotorTorque = this.MOTORS_TORQUE; |
|
rjd.motorSpeed = 1; |
|
rjd.lowerAngle = -0.3 * Math.PI; |
|
rjd.upperAngle = 0.3 * Math.PI; |
|
joint_motor = world.CreateJoint(rjd); |
|
joint_motor.SetUserData(new CustomMotorUserData("knee", SPEED, true, 0.0, body_p3)); |
|
this.motors.push(joint_motor); |
|
|
|
|
|
let fin_fd = new b2.FixtureDef(); |
|
fin_fd.shape = new b2.PolygonShape(); |
|
vertices = []; |
|
for(let vertex of FIN){ |
|
vertices.push(new b2.Vec2(vertex[0] / this.SCALE, vertex[1] / this.SCALE)); |
|
} |
|
fin_fd.shape.Set(vertices, FIN.length); |
|
fin_fd.density = this.DENSITY; |
|
fin_fd.restitution = 0.0; |
|
fin_fd.filter.categoryBits = 0x20; |
|
fin_fd.filter.maskBits = 0x000F; |
|
|
|
let fin_positions = [ |
|
[init_x, init_y - 22 / 2 / this.SCALE + 0.2], |
|
]; |
|
let fin_angle = -0.2 * Math.PI; |
|
let middle_fin_x_distance = Math.sin(fin_angle) * 20 / 2 / this.SCALE; |
|
let middle_fin_y_distance = Math.cos(fin_angle) * 20 / 2 / this.SCALE; |
|
|
|
for(let fin_pos of fin_positions){ |
|
let current_fin_x = fin_pos[0] + middle_fin_x_distance; |
|
let current_fin_y = fin_pos[1] - middle_fin_y_distance; |
|
|
|
let fin_bd = new b2.BodyDef(); |
|
fin_bd.type = b2.Body.b2_dynamicBody; |
|
fin_bd.position.Set(current_fin_x, current_fin_y); |
|
let fin = world.CreateBody(fin_bd); |
|
fin.CreateFixture(fin_fd); |
|
fin.color1 = "#806682"; |
|
fin.color2 = "#4D4D80"; |
|
fin.SetUserData(new CustomBodyUserData(true, false, "fin")); |
|
this.body_parts.push(fin); |
|
this.fins.push(fin); |
|
|
|
|
|
rjd = new b2.RevoluteJointDef(); |
|
rjd.Initialize(hull, fin, new b2.Vec2(fin_pos[0], fin_pos[1])); |
|
rjd.enableMotor = true; |
|
rjd.enableLimit = true; |
|
rjd.maxMotorTorque = this.MOTORS_TORQUE; |
|
rjd.motorSpeed = 1; |
|
rjd.lowerAngle = -0.3 * Math.PI; |
|
rjd.upperAngle = 0.2 * Math.PI; |
|
joint_motor = world.CreateJoint(rjd); |
|
joint_motor.SetUserData(new CustomMotorUserData("shoulder", SPEED, true, 0.0, fin)); |
|
this.motors.push(joint_motor); |
|
} |
|
} |
|
} |