Created
October 6, 2017 09:42
-
-
Save whitetigle/76b7b0386b8d26173dfa0ed63d049d2f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Pixi | |
open System | |
open Fable.Core | |
open Fable.Core.JsInterop | |
open Fable.Import | |
open Fable.Import.Pixi | |
open Fable.Import.Browser | |
open Fable.Import.JS | |
open Fable.Pixi | |
open Elmish | |
open Hink | |
type Msg = | |
| OnMove of float * float | |
(* ------------------------ COGS ---------------------*) | |
[<Literal>] | |
let cogWidth = 100. | |
[<Literal>] | |
let cogsCount = 20 | |
module Cog = | |
let texture = PIXI.Texture.fromImage("../img/cog.png") | |
type Size = | |
| Tiny | |
| Small | |
| Medium | |
| Large | |
type CogData = { | |
Size: Size | |
mutable Target: float*float | |
mutable StartPosition: float*float | |
mutable IsDragging: bool | |
mutable Interaction : PIXI.interaction.InteractionData option | |
} | |
// create a balanced list of cogs sizes | |
let cogSizes = | |
// Create a table of cog size using some probabilities | |
let distribution = [| | |
for i in 0..cogsCount do | |
let rand = Math.random() | |
yield | |
match rand with | |
| x when x > 0. && x < 0.3 -> Tiny | |
| x when x >= 0.3 && x < 0.7 -> Small | |
| x when x >= 0.7 && x < 0.9 -> Medium | |
| _ -> Large | |
|] | |
// shuffle our table | |
let rand = new System.Random() | |
let swap (a: _[]) x y = | |
let tmp = a.[x] | |
a.[x] <- a.[y] | |
a.[y] <- tmp | |
// shuffle an array (in-place) | |
let al = distribution.Length | |
let shuffle a = | |
List.iteri (fun i _ -> swap a i (rand.Next(i, al))) !!a | |
shuffle distribution | |
distribution | |
// scale cog according to kind | |
let scaleFactor size = | |
match size with | |
| Tiny -> 0.25 | |
| Small -> 0.5 | |
| Medium -> 0.75 | |
| Large -> 1.0 | |
let make kind = | |
// Extended Sprite from Fable.Pixi allows to add custom data inside our Sprite | |
let cog = ExtendedSprite(texture, { StartPosition=(0.,0.); Target=(0.,0.); Size= kind; IsDragging = false; Interaction = None }) | |
// scale our cog according to its kind | |
let scaleFactor = scaleFactor cog.Data.Size | |
let scale : PIXI.Point = !!cog.scale | |
scale.x <- scaleFactor | |
scale.y <- scaleFactor | |
// center the cog's anchor point | |
cog.anchor.set(0.5) | |
cog | |
let moveTo x y (cog:ExtendedSprite<CogData>) = | |
// place our cog on screen | |
let position : PIXI.Point = !!cog.position | |
position.x <- x | |
position.y <- y | |
cog.Data.StartPosition <- (x,y) | |
cog | |
let addTarget x y (cog:ExtendedSprite<CogData>) = | |
// place our cog on screen | |
cog.Data.Target <- (x,y) | |
cog | |
// cast back to ExtendedSprite since attachEvent returns a Sprite | |
let backTo (sprite: PIXI.Sprite) = | |
sprite :?> ExtendedSprite<CogData> | |
(* ------------------------ GAME ---------------------*) | |
module Dock = | |
open Cog | |
let prepare container renderer= | |
// create our cogs for our dock | |
let rec makeCogs cogs totalWidth (list:ExtendedSprite<Cog.CogData> list) (container:PIXI.Container) (renderer: PIXI.WebGLRenderer )= | |
let dockY = renderer.height * 0.9 | |
match cogs with | |
| [] -> // center our cogs on screen | |
let margin = (renderer.width - totalWidth) * 0.5 | |
list | |
|> List.map( fun cog -> | |
let (x,y) = cog.Data.StartPosition | |
(Cog.moveTo (x+margin) y >> Cog.backTo) cog | |
) | |
| head :: remains -> // add next cog to the dock | |
let margin = 30. | |
let width = (Cog.scaleFactor head) * cogWidth + margin | |
(* | |
// enable the cog to be interactive... this will allow it to respond to mouse and touch events | |
cog.interactive <- true | |
// this button mode will mean the hand cursor appears when you roll over the cog with your mouse | |
cog.buttonMode <- true | |
// attach our custom events to enable dragging | |
cog | |
|> attachEvent Pointerdown (onDragStart cog) | |
|> attachEvent Pointerup (onDragEnd cog) | |
|> attachEvent Pointermove (onDragMove cog targets) | |
|> backTo | |
*) | |
let cog = | |
Cog.make head | |
|> Cog.moveTo totalWidth dockY | |
makeCogs remains (totalWidth+width) (list @ [cog]) container renderer | |
makeCogs | |
[Cog.Tiny;Cog.Small;Cog.Medium;Cog.Large] 0. [] container renderer | |
type Model = { | |
Cogs : ExtendedSprite<Cog.CogData> list | |
Targets : ExtendedSprite<Cog.CogData> list | |
CurrentPosition : (float * float) option | |
} | |
module App = | |
let init() = | |
// our view | |
let options = jsOptions<PIXI.ApplicationOptions> (fun o -> | |
o.backgroundColor <- Some 0x000000 | |
) | |
let app = PIXI.Application(800., 600., options) | |
Browser.document.body.appendChild(app.view) |> ignore | |
let renderer : PIXI.WebGLRenderer = !!app.renderer | |
// our layers | |
// markers layer | |
let targetsContainer = PIXI.Container() | |
app.stage.addChild targetsContainer |> ignore | |
// found cogs layer | |
let cogsContainer = PIXI.Container() | |
app.stage.addChild cogsContainer |> ignore | |
let max = Cog.cogSizes.Length | |
let startX = 0. | |
let startY = 0. | |
let maxWidth = renderer.width * 0.8 | |
let rec addCog index (totalWidth,totalHeight) first previous cogs = | |
match index with | |
| idx when idx < max -> // check whether we can add one more cogs | |
if totalWidth < maxWidth then // check wether we have enough space laeft | |
let kind = Cog.cogSizes.[index] | |
let currentCog = Cog.make kind | |
match previous with | |
| Some (previousCog:ExtendedSprite<Cog.CogData>) -> | |
// we want to move our new cog just next to the previous | |
let (firstX,firstY) = first | |
let (prevX,prevY) = previousCog.Data.Target | |
let prevRadius = cogWidth * (Cog.scaleFactor previousCog.Data.Size) * 0.5 | |
let currentRadius = cogWidth * (Cog.scaleFactor currentCog.Data.Size) * 0.5 * 1.1 | |
let distance = (prevRadius + currentRadius) |> Math.floor | |
let angle = 340. * PIXI.Globals.DEG_TO_RAD | |
let newX = prevX + Math.cos(angle) * distance | |
let newY = firstY + Math.sin(angle) * distance | |
let current = currentCog |> Cog.addTarget newX newY | |
let totalWidth = newX + currentRadius | |
let totalHeight = newY + currentRadius | |
addCog (idx+1) (totalWidth,totalHeight) first (Some current) (cogs @ [current]) | |
| None -> // very first cog | |
let (x,y) = first | |
let current = currentCog |> Cog.addTarget x 0. | |
let currentRadius = cogWidth * (Cog.scaleFactor currentCog.Data.Size) * 0.5 | |
let totalWidth = x + currentRadius | |
let totalHeight = y + currentRadius | |
addCog (idx+1) (totalWidth,totalHeight) first (Some current) (cogs @ [current]) | |
else // we don't have enough space to fill all our cogs | |
cogs, (totalWidth,totalHeight) | |
| _ -> // we have enough cogs | |
cogs, (totalWidth,totalHeight) | |
// build our cogs list | |
let targets,(totalWidth,totalHeight) = addCog 0 (0.,0.) (startX,startY) None [] | |
// place our markers | |
let addMarker xMargin yMargin startY (cog:ExtendedSprite<Cog.CogData>) = | |
let (x,y) = cog.Data.Target | |
let target = PIXI.Sprite.fromImage("../img/target.png") | |
// center the cog"s anchor point | |
target.anchor.set(0.5) | |
let position : PIXI.Point = !!target.position | |
position.x <- x + xMargin // center our cogs on x axis | |
position.y <- startY + y - yMargin // center our cogs on y axis | |
targetsContainer.addChild target |> ignore | |
cog | |
// place our cogs on top | |
let addCogs xMargin yMargin startY (cog:ExtendedSprite<Cog.CogData>) = | |
let (x,y) = cog.Data.Target | |
let position : PIXI.Point = !!cog.position | |
position.x <- x + xMargin // center our cogs on x axis | |
position.y <- startY + y - yMargin // center our cogs on y axis | |
cogsContainer.addChild cog | |
// center our markers | |
let xMargin = (renderer.width - totalWidth) * 0.5 | |
let yMargin = totalHeight * 0.5 | |
let targets = | |
targets | |
|> List.map (addMarker xMargin yMargin (renderer.height*0.5)) | |
// |> List.map (addCogs xMargin yMargin (renderer.height*0.5) ) | |
// dragging cogs layer | |
let dockContainer = PIXI.Container() | |
app.stage.addChild dockContainer |> ignore | |
let cogs = Dock.prepare dockContainer renderer | |
let onDragStart (cog: ExtendedSprite<Cog.CogData>) (ev:PIXI.interaction.InteractionEvent) = | |
printfn "in" | |
cog.alpha <- 0.5 | |
cog.Data.Interaction <- Some ev.data | |
cog.Data.IsDragging <- true | |
let onDragEnd (cog: ExtendedSprite<Cog.CogData>) _ = | |
cog.alpha <- 1. | |
cog.Data.Interaction <- None | |
cog.Data.IsDragging <- false | |
let position : PIXI.Point = !!cog.position | |
let startX, startY = cog.Data.StartPosition | |
position.x <- startX | |
position.y <- startY | |
let onDragMove (cog: ExtendedSprite<Cog.CogData>) _ = | |
printfn "in" | |
if cog.Data.IsDragging then | |
if cog.Data.Interaction.IsSome then | |
let interaction = cog.Data.Interaction.Value | |
let localPosition : PIXI.Point = interaction.getLocalPosition(cog.parent) | |
let position : PIXI.Point = !!cog.position | |
position.x <- localPosition.x | |
position.y <- localPosition.y | |
let backTo (sprite: PIXI.Sprite) = | |
sprite :?> ExtendedSprite<Cog.CogData> | |
// add events | |
let cogs = | |
cogs | |
|> List.map( fun cog -> | |
cog.interactive <- true | |
cog.buttonMode <- true | |
cog | |
|> attachEvent Pointerdown (onDragStart cog) | |
|> attachEvent Pointerup (onDragEnd cog) | |
|> attachEvent Pointermove (onDragMove cog) | |
|> dockContainer.addChild | |
|> backTo | |
) | |
{ | |
Cogs = cogs | |
Targets = targets | |
CurrentPosition = None | |
},[] | |
let update (msg: Msg) (model: Model) = | |
model, [] | |
let onUpdate (model:Model) _= | |
printfn "%A" model.CurrentPosition | |
(* | |
let timer initial = | |
let sub dispatch = | |
window.setInterval(fun _ -> | |
dispatch (OnMove (0.,0.) | |
, 1000) |> ignore | |
Cmd.ofSub sub | |
*) | |
// Start Elmish! | |
let start() = | |
Program.mkProgram init update onUpdate | |
|> Program.run | |
App.start() | |
(* | |
// our render loop | |
let tick delta = | |
let l = cogsContainer.children.Count | |
for i in 0..l do | |
let currenTarget = cogsContainer.children.[i] | |
currenTarget.rotation <- currenTarget.rotation + 0.1 | |
app.ticker.add(tick) |> ignore | |
*) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment