{"id":"zachcurtis/easybullet","name":"easybullet","scope":"zachcurtis","platform":"roblox","description":"A simple bullet runtime that handles network replication, network syncing, and adjusts the rendered bullets by client framerate.","version":"0.3.3","latest":"0.3.3","versions":["0.3.2","0.3.3"],"license":"MIT","licenseRating":"safe","licenseCaveats":[],"licenseVerified":true,"dependencies":{},"integrity":"e0783076b823ec9088a759a766fd4bcd4ff1cc36ae71bba900f1982a12cea713","likes":0,"downloads":0,"install":"forest install zachcurtis/easybullet","url":"https://forest.dev/p/roblox/zachcurtis/easybullet","files":"https://api.forest.dev/ai/package/roblox/zachcurtis/easybullet/files","readme":"# EasyBullet\r\n\r\nA simple bullet runtime that handles network replication, network syncing, and adjusts the rendered bullets by client framerate. \r\n\r\n## Getting Started\r\nDownload the [EasyBullet.rbxmx](https://github.com/ZachCurtis/EasyBullet/blob/main/EasyBullet.rbxmx) file and drag it into studio\r\n\r\nOr grab the [Marketplace model](https://create.roblox.com/marketplace/asset/13513545189/EasyBullet) and insert it via the Toolbox window\r\n\r\nTo build EasyBullet into a model, use:\r\n\r\n```bash\r\nrojo build -o \"EasyBullet.rbxmx\" build.project.json\r\n```\r\n\r\nTo serve EasyBullet into your game, use:\r\n```bash\r\nrojo serve\r\n```\r\n\r\nTo install using wally, add to your wally.toml dependencies:\r\n```toml\r\nEasyBullet = \"zachcurtis/easybullet@0.3.3\"\r\n```\r\nThen run:\r\n```bash\r\nwally install\r\n```\r\n\r\n## Example\r\n```lua\r\nlocal EasyBullet = require(path.To.EasyBullet)\r\n\r\nlocal defaultSettings = {\r\n    Gravity = true,\r\n    RenderBullet = true,\r\n    BulletColor = Color3.new(0.945098, 0.490196, 0.062745),\r\n    BulletThickness = .1,\r\n    FilterList = {},\r\n    FilterType = Enum.RaycastFilterType.Exclude,\r\n    BulletPartProps = {},\r\n    BulletData = {}\r\n}\r\n\r\nlocal easyBullet = easyBullet.new(defaultSettings)\r\n\r\neaseBullet:FireBullet(barrelPosition, bulletVelocity)\r\n\r\neasyBullet.BulletHitHumanoid:Connect(function(shootingPlayer, rayResult, hitHumanoid)\r\n    hitHumanoid:TakeDamage(15)\r\nend)\r\n```\r\n\r\n## API\r\n\r\nEasyBulletSettings\r\n```lua\r\nexport type EasyBulletSettings = {\r\n    Gravity: boolean?, -- Should the bullet curve according to workspace.Gravity\r\n    RenderBullet: boolean?, -- Should EasyBullet display a rendered bullet on the client\r\n    BulletColor: Color3?, -- Sets the color of the bullets rendered\r\n    BulletThickness: number?, -- Sets the thickness of the bullets in studs\r\n    FilterList: {[number]: Instance}?, -- An array of instances assigned to RayParams.FilterDescendantsInstances\r\n    FilterType: Enum.RaycastFilterType?, -- The RaycastFilterType, either Include or Exclude\r\n    BulletPartProps: {[string]: unknown}?, -- A dictionary of properties matching the properties of BasePart to override the bullet part rendering. Cannot include keys \"CFrame\", \"Size\", or \"Color\"\r\n    BulletData: {[string]: unknown}? -- A dictionary of any data you wish to associate with this bullet. HitVelocity and BulletId are reserved keys for this table, and are set by EasyBullet before passing the BulletData table to the BulletHit, BulletHitHumanoid, and BulletUpdated events. Useful for variations such as displaying a different hit effect for a sniper, or altering the damage dependent on the gun type.\r\n}\r\n```\r\n#### Default EasyBulletSettings\r\n| Field   | Type    | Default |\r\n| ------- | ------- | ------- |\r\n| Gravity | boolean | true    |\r\n| RenderBullet | boolean | true |\r\n| BulletColor | Color3 | Color3.new(0.945098, 0.490196, 0.062745) |\r\n|  BulletThickness | number | .1 |\r\n| FilterList | { [number]: Instance } | {} |\r\n| FilterType | [RaycastFilterType](https://create.roblox.com/docs/reference/engine/enums/RaycastFilterType) | Enum.RaycastFilterType.Exclude |\r\n| BulletPartProps | { [string]: unknown } | {} |\r\n| BulletData | { [string]: unknown } | {} |\r\n\r\n\r\n### Methods\r\nConstructor - EasyBullet is a singleton so it will only be constructed once per server or client, but any subsequent calls to EasyBullet.new will return the constructed singleton. Only the settings overrides passed to the first constructor will be used.\r\n```lua\r\nlocal EasyBulletSettingsOverrides = {\r\n    BulletColor = Color3.new(1, 0, 0),\r\n    Gravity = false\r\n}\r\n\r\nlocal easyBullet = EasyBullet.new(EasyBulletSettingsOverrides)\r\n```\r\n\r\nFireBullet - call on client to fire a bullet for a player, or on the server to fire a bullet for a NPC\r\n```lua\r\nlocal direction = mouse.Hit.Position - gun.BarrelPosition\r\nlocal velocity = direction.Unit * 400\r\n\r\nlocal optionalEasyBulletSettings = {\r\n    BulletThickness = .4\r\n}\r\n\r\neasyBullet:FireBullet(gun.BarrelPosition, velocity, optionalEasyBulletSettings)\r\n```\r\n\r\nBindCustomCast - pass a callback that returns a RaycastResult or nil to implement custom raycast behavior, such as lag compensation for network delayed character positions\r\n```lua\r\neasyBullet:BindCustomCast(function(shooter: Player?, lastFramePosition: Vector3, thisFramePosition: Vector3, elapsedTime: number, bulletData: {[string]: Unknown})\r\n    local direction = lastFramePosition - thisFramePosition\r\n\r\n    local raycastParams = RaycastParams.new()\r\n\r\n    -- npc shots have no shooting player\r\n    if shooter then\r\n        raycastParams.FilterDescendantsInstances = {shooter.Character}\r\n        raycastParams.FilterType = Enum.RaycastFilterType.Exclude\r\n    end\r\n\r\n    return workspace:Raycast(lastFramePosition, direction, raycastParams)\r\nend)\r\n```\r\n\r\nBindShouldFire - pass a callback that returns a boolean to provide a means of filtering bullets before they're fired. The bullet will initially be networked before the ShouldFire callback is called, but the bullet will automatically clean it's self up across the network if the ShouldFire callback returns false.\r\n```lua\r\neasyBullet:BindShouldFire(function(shooter: Player?, barrelPosition: Vector3, velocity: Vector3, ping: number, easyBulletSettings: Bullet.EasyBulletSettings?)\r\n    if not shooter or not shooter.Character or not shooter.Character.HumanoidRootPart then\r\n        return false\r\n    end\r\n\r\n    local humanoid = shooter.Character.Humanoid\r\n    local rootPart = shooter.Character.HumanoidRootPart\r\n\r\n    local discrepancy = (barrelPosition - rootPart.Position).Magnitude\r\n    local desyncTolerance = (shooter:GetNetworkPing() * humanoid.WalkSpeed) * 1.2\r\n\r\n    -- return true if barrelPosition is within how far the player could have walked in that time\r\n    return discrepancy <= desyncTolerance\r\nend)\r\n``` \r\n\r\n### Events\r\n\r\nBulletHit - fired whenever a bullet hits something\r\n```lua\r\neasyBullet.BulletHit:Connect(function(shootingPlayer: Player?, raycastResult: RaycastResult, bulletData: {[string]: Unknown} | {HitVelocity: Vector3})\r\n    print(raycastResult.Instance.Name)\r\nend)\r\n```\r\n\r\nBulletHitHumanoid - fired whenever a bullet hits a part belonging to a model with a child humanoid\r\n```lua\r\neasyBullet.BulletHitHumanoid:Connect(function(shootingPlayer: Player?, raycastResult: RaycastResult, hitHumanoid: Humanoid, bulletData: {[string]: Unknown} | {HitVelocity: Vector3})\r\n    hitHumanoid:TakeDamage(15)\r\nend)\r\n```\r\n\r\nBulletUpdated - fired every time the bullet updates. Useful for rendering custom bullets.\r\n```lua\r\neasyBullet.BulletUpdated:Connect(function(lastFramePosition: Vector3, thisFramePosition: Vector3, bulletData: {[string]: unknown})\r\n    local direction = lastFramePosition - thisFramePosition\r\n\r\n    bulletPart.Size = Vector3.new(.2, .2, direction.Magnitude)\r\n    bulletPart.CFrame = CFrame.lookAt(lastFramePosition, thisFramePosition) * CFrame.new(0,0, -direction.Magnitude * .5)\r\nend)\r\n```","readmeTruncated":false}